Building a To-Do App with Realm

Today’s blogpost is contributed by one of our users: Piet Brauer, who gave a talk about Realm at his local meetup group in Hamburg just a few weeks after we launched! Piet is a mobile developer at XING a publicly-traded German company building a social platform for professionals. You can find Piet on GitHub and Twitter.
If you’d like to share your technical tutorial about Realm, please email Arwa!


At the local CocoaHeads meeting in Hamburg, I lead a walkthrough on how to implement a small shopping list/to-do app using Realm and Swift. There are a lot of tutorials out there on how to accomplish this task using NSCoding, CoreData, Plists and so on, but I wanted to share one for Realm because the process is so easy. Here’s my walkthrough:

1. Set up a new Xcode project

Create a new Xcode project and add a simple UITableViewController:

// AppDelegate.swift

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
   var window: UIWindow?

   func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
       window = UIWindow(frame: UIScreen.mainScreen().bounds)
       window?.backgroundColor = UIColor.whiteColor() // [1]

       let viewController = ViewController(nibName: nil, bundle: nil) // [2]
       let navController = UINavigationController(rootViewController: viewController) // [3]

       window?.rootViewController = navController // [4]
       window?.makeKeyAndVisible()
       return true
   }
}

Get more development news like this

Delete the Storyboard and add everything in code.

  1. In application:didFinishLaunchingWithOptions:, create a new UIWindow and set its backgroundColor.
  2. Create a new instance of the ViewController class, which will host the list of items in the list.
  3. Put the ViewController instance in a UINavigationController. It looks nice and you’ll use it later for adding new items.
  4. Set the navController as the rootViewController and make the window visible.
// ViewController.swift

import UIKit

class ViewController: UITableViewController {

}

This class should be self-explanatory, as it is just a subclass of UITableViewController and will be used as a placeholder for the time being.

Your app should look like this now:

2. Integrate Realm

Since Swift is still in beta and Swift sources are not fully available via CocoaPods yet, you have to integrate it the “old” way. But the awesome Realm team has you covered with their simple tutorial, which will be updated with every beta.

3. Create the model

Models in Swift combined with Realm are really easy to do. Create a new class and add the following code to it:

// ToDoItem.swift

import Realm // [1]

class ToDoItem: RLMObject { // [2]
   dynamic var name = "" // [3]
   dynamic var finished = false
}
  1. Note that you have to import the Realm module here.
  2. To make your model a Realm-accessible model, you have to inherit from RLMObject.
  3. Every property that should be fetched and stored in the database also needs the dynamic keyword. Also, set the default values for the properties.

4. Display items in the list

Next, use the table view to display all items from the list:

// ViewController.swift

import UIKit
import Realm

class ViewController: UITableViewController {
   var todos: RLMArray {
       get {
           return ToDoItem.allObjects()
       }
   }
}

First, define a new variable and set its class to RLMArray. To make sure you use the latest data from your database, you can use a really nice feature of Realm. Just take your model class and call allObjects() on it. This will hit the database and give you a read-only RLMArray instance. You can basically use this array like a normal Swift array or an NSArray.

// ViewController.swift

...

override func viewDidLoad() {
   super.viewDidLoad()
   tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "CellIdentifier") // [1]
}

override func viewWillAppear(animated: Bool) {
   super.viewWillAppear(animated)
   tableView.reloadData() // [2]
}

override func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
   return Int(todos.count) // [3]
}

override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
   let cell = tableView.dequeueReusableCellWithIdentifier("CellIdentifier", forIndexPath: indexPath) as UITableViewCell

   let index = UInt(indexPath.row)
   let todoItem = todos.objectAtIndex(index) as ToDoItem // [4]
   cell.textLabel.text = todoItem.name // [5]

   return cell
}

Now I will walk you through the code that is responsible for filling the table view with data:

  1. Register the UITableViewCell class to be dequeued by the table view. This needs to be done to have a performant app.
  2. Every time you visit the ViewController, you want to see the latest data. That is why you have to reload the table view each time.
  3. Next up, the table view needs to know how many items it should display. Access your todos variable and return its count.
  4. Dequeue your registered cell, get the index of the current cell and fetch the corresponding object from your database.
  5. Set the name of your fetched ToDoItem to the cell’s textLabel.

Unfortunately if you run your app now, you won’t see any changes because the database is currently empty.

###5. Add a view controller to take the input

Now that you have a table view that displays the data, you need to insert data into the database. Create a new Swift file and name it AddViewController. There you are going to implement a UITextField which will take the input:

// AddViewController.swift

import UIKit
import Realm

class AddViewController: UIViewController, UITextFieldDelegate {
   var textField: UITextField?
   var newItemText: String?

   override func viewDidLoad() { // [1]
       super.viewDidLoad()
       view.backgroundColor = UIColor.whiteColor()
       setupTextField()
       setupNavigationBar()
   }

   override func viewDidAppear(animated: Bool) { // [2]
       super.viewDidAppear(animated)
       textField?.becomeFirstResponder()
   }

   func setupTextField() { // [3]
       textField = UITextField(frame: CGRectZero)
       textField?.placeholder = "Type in item"
       textField?.delegate = self
       view.addSubview(textField!)
   }

   func setupNavigationBar() { // [4]
       navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Done, target: self, action: "doneAction")
   }

   override func viewDidLayoutSubviews() { // [5]
       super.viewDidLayoutSubviews()
       let padding = CGFloat(11)
       textField?.frame = CGRectMake(padding, self.topLayoutGuide.length + 50, view.frame.size.width - padding * 2, 100)
   }

   func doneAction() { // [6]
       let realm = RLMRealm.defaultRealm() // [6.1]
       if self.textField?.text.utf16Count > 0 { // [6.2]
           let newTodoItem = ToDoItem() // [6.3]
           newTodoItem.name = self.textField!.text
           realm.transactionWithBlock(){ // [6.4]
               realm.addObject(newTodoItem)
           }
       }
       dismissViewControllerAnimated(true, completion: nil) // [7]
   }

   func textFieldShouldReturn(textField: UITextField!) -> Bool { // [8]
       doneAction()
       textField.resignFirstResponder()
       return true
   }
}
  1. In the viewDidLoad() method, create your text field and add a Done button to the navigation bar.
  2. In viewDidAppear(), tell the text field to become the first responder. This will show the keyboard right after the view is visible.
  3. Setting up your text field is pretty straightforward. Initialize it, set a placeholder if no text is entered and add it to your view. You should also set the delegate; this is needed to know when the user hits the return key.
  4. In setupNavigationBar(), create a Done button and add it as the right bar button.
  5. In viewDidLayoutSubviews(), set the frame of your text field to have a little padding, left and right.
  6. When the user taps the Done button or hits return on the keyboard, the doneAction() is called. The purpose of the doneAction() is to check for the entered text and write it to the database. These are the steps needed:
    1. Get the default realm. You can think of it as the database you want to write in.
    2. Check the text field for text that has been inserted into it. If it is empty, just dismiss the view controller.
    3. Create a new ToDoItem instance. Please note that this is not any different from other objects in Swift.
    4. This is the most interesting part of the method and may need some further explanation. In order to change values in the database, you have to prepare it. This can be done using a transactionBlock or using beginWriteTransaction() and commitWriteTransaction(). I opt for the block, as I find it more elegant to use. Inside the block, add the newly created ToDo item into your realm database.
  7. After everything is done, dismiss the view controller and you’re back on the list.
  8. This is the text field’s callback if the user hits the return key. In here, we call it doneAction().

Your example should be able to do something like this:

(Please note that not all features of the full example were covered in this walkthrough. I added sections for finished items and setting an item to finished and unfinished. The corresponding code is written in the ViewController.swift class. You can find the full working example on my GitHub page.)

You now have a fully working to-do list. For the very first time in iOS development, it is that easy to integrate a database. The setup code alone of CoreData would require more lines than this full example.

@pietbrauer

Next Up: Realm for iOS #3: Building a Simple Swift App With Fine-Grained Notifications

General link arrow white

About the content

This content has been published here with the express permission of the author.


Piet Brauer

Building @Git2Go and http://velkom.io writing http://mobileonly.tools . Obsessed with the future of mobile productivity.

4 design patterns for a RESTless mobile integration »

close