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
}
}
Delete the Storyboard and add everything in code.
- In
application:didFinishLaunchingWithOptions:
, create a newUIWindow
and set itsbackgroundColor
. - Create a new instance of the
ViewController
class, which will host the list of items in the list. - Put the
ViewController
instance in aUINavigationController
. It looks nice and you’ll use it later for adding new items. - Set the
navController
as therootViewController
and make thewindow
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
}
- Note that you have to import the Realm module here.
- To make your model a Realm-accessible model, you have to inherit from
RLMObject
. - 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:
- Register the
UITableViewCell
class to be dequeued by the table view. This needs to be done to have a performant app. - 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.
- Next up, the table view needs to know how many items it should display. Access your
todos
variable and return its count. - Dequeue your registered cell, get the index of the current cell and fetch the corresponding object from your database.
- 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
}
}
- In the
viewDidLoad()
method, create your text field and add a Done button to the navigation bar. - In
viewDidAppear()
, tell the text field to become the first responder. This will show the keyboard right after the view is visible. - 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.
- In
setupNavigationBar()
, create a Done button and add it as the right bar button. - In
viewDidLayoutSubviews()
, set the frame of your text field to have a little padding, left and right. - When the user taps the Done button or hits return on the keyboard, the
doneAction()
is called. The purpose of thedoneAction()
is to check for the entered text and write it to the database. These are the steps needed:- Get the default realm. You can think of it as the database you want to write in.
- Check the text field for text that has been inserted into it. If it is empty, just dismiss the view controller.
- Create a new
ToDoItem
instance. Please note that this is not any different from other objects in Swift. - 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 usingbeginWriteTransaction()
andcommitWriteTransaction()
. 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.
- After everything is done, dismiss the view controller and you’re back on the list.
- 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.
Receive news and updates from Realm straight to your inbox