After the big evolution in smart phones in the last years, a lot of tools have been developed to make life easier and simpler for developers to deliver the best performance and quality. To rock in the app store today is not an easy job. And to make your app ready for scalability is more harder. When you succeed in have millions of users to your app, you have to care about everything in your app and perform all operations in no time. So one of the problems that face many developers nowadays, is dealing with Database. It really causes a terrible headache for each one of us and I think nowadays you hav only two options: SQLite and Core Data. I was a big fan of Core Data and its power to deal with records and persisting data but I realized that I waste much time dealing with it while developing apps. Recently, I have come across with Realm, a new better replacement for SQLite and Core Data.
What is Realm?
Realm is a cross platform mobile database for iOS (available in Swift & Objective-C) and Android. Realm is built to be better and faster than SQLite and Core Data. It is not just better or faster, it’s also easier to use and you can do a lot of things with just few lines of code. Realm is totally free and you can use it without any limits. Realm is built to target mobile apps only as in the last decade we didn’t notice any innovation in mobile databases. Now to deal with mobile database, you have only one options which is SQLite or any wrapper that uses SQLite behind the scene like Core Data. Realm is designed to be easy to use as it not an ORM and it uses its own persistence engine to give you the performance and speed.
Why Realm?
Realm is incredibly fast and easy to use and you will see everything you need will be accomplished with just couple of lines of code regardless it’s reading or writing to database. Here I will try to collect all advantages and reasons for why making Realm is the best choice to work with database in your mobile app:
- Easy Installation: As you will see in next section about working with Realm. Installing Realm is more easy as you imagine. With simple command in Cocoapods, you here to go to work with Realm.
- Speed: Realm is incredibly fast library to work with database. Realm is faster than SQLite and CoreData and the benchmarks here are the best evidence for that.
- Cross Platform: Realm database files are cross platform and can be shared among iOS and Android. Regardless you work with Java, Objective-C, or Swift, you will use your high-level models.
- Scalability: scalability is very important to be considered while developing your mobile app specially if your app deals with large number of users and massive number of records. You should consider that from the beginning while designing and choosing your tools to be used. Realm is ready for scalability and work with large data in no time. You will bring speed and smoothing to your app while using Realm.
- Good documentation & Support: Realm team has provided readable, well organized and rich documentation about Ream. If you have any problems you can reach them via Twitter, Github or Stackoverflow.
- Trusted: Realm has been used by giant of startups and companies in their mobile apps like Pinterest, Dubsmash, and Hipmunk.
- Free: with all these awesome features, Realm is completely free.
Getting Started
Let’s get started with Realm tutorial and use it to build a simple iPhone app with Swift. The demo project will be a simple Todo application. User can add Task Lists and each list can contain multiple tasks. The task has a title, notes, due date, attachment image, and a boolean indicate its completed or not. Before starting with Xcode project. We need to first configure Xcode and install the tools needed to work with Realm.
Prerequisites
Please consider the following prerequisites:
- iOS 8 or later, OS X 10.9 or later.
- Xcode 6.3 or later.
- Realm has two releases, one for Swift 2.0 and one for Swift 1.2. We recommend using Realm for Swift 2.0. You can use it for Swift 1.2 but it will not be longer supported with Realm team in the future so to be safe use the release of Swift 2.0.
Configuring Xcode and Tools needed
Before configuring the Xcode project, please make sure that you have installed CocoaPods in your computer as we will use it for installing Realm in the Xcode project. If you are not familiar with CocoaPods, you can check online for several tutorials to give you all information you need for getting started with it.
Now, create a new Xcode project with the template “Single View Application” and name it “RealmTasks” or whatever you like. Please make sure that Swift is selected as a programming language. Now navigate to your project directory in terminal and execute the following command to initialize the CocoaPods:
pod init
Then open the pod file generated with Xcode and edit it to add pod ‘RealmSwift’ right after your target, so it should be something like this:
Next run the command “pod install” to download the install Realm in your project. After finishing, you will see a new Xcode workspace has been generate beside your project file. Please open the workspace RealmTasks.xcworkspace and don’t open the xcodeproj. After you open the workspace, you should see something like this:
Now Xcode is ready to work with Realm, but we will install the following tools to make things easier while working with Realm.
Installing Realm plugin in Xcode
Realm team has provided a very useful plugin for Xcode that will be used for generating Realm models. To install the plugin we will use Alcatraz. For people who don’t know what is Alcatraz, it’s a very simple and useful open source package manager to automatically install plugins, templates, or colors in Xcode without making your hands dirty with manual configurations. To install Alcatraz just paste the following command in your terminal and then restart your Xcode:
curl -fsSL https://raw.githubusercontent.com/supermarin/Alcatraz/master/Scripts/install.sh | sh
Then in Xcode, select Window and select Package Manager, like the following image:
Then a popup window will open to choose which plugin or template to install in Xcode, in search bar you can search for any plugins or color templates to customize your Xcode. Write “Realm” in the search bar and a “RealmPlugin” plugin will appear, then click on Install.
Realm Browser
The last tool I want to mention is Realm browser. This browser helps you to read and edit your .realm databases files. These files are created in your app with all information about entities, attributes and records inside database tables. We said before that these files can be shared among different platform like iOS or Android. To download the realm browser tool please visit iTunes store to download the latest version. Open the app then choose Tools -> Generate demo database. It will generate test database realm file to you and you can open it and see its content with the browser. You should see something like this when you open your demo database:
As you see in the Class RealmTestClass1, it has 1000 records and it shows the different types of parameters (columns) of this class. We will talk about support property types in next sections.
Now everything is prepared for working with Realm. Let’s get started.
Database Model Classes
Now game on! First we will create the model classes or our database. To create Realm model classes, you simply create normal Swift classes that extend the Object class. Think of Object as the base class for all Realm model classes, as well you can extend any class that extend Object at the end. Once you create your class, of course you need properties. Realm supports variety types of properties as follows:
– Int, Int8, Int16, Int32, and Int64
– Boolean
– Float
– String
– NSDate
– NSData
– Class extends Object => Used for One-to-one relations
– List<Object> => Used for one-to-many relations
List as Realm class to hold collection of the Object instances, refer to the screenshot of the demo database, the last column was just an array of pointers for existing records in another table. While working with Realm model classes, you have to know that you can deal with them like any other Swift classes. For example, you can add methods or protocols and use them like any other Swift class.
Talk is cheap, show me the code 🙂
Okay, now let’s use the Realm plugin that we have installed in Xcode to create the Realm classes. Go to Xcode and create a new file. In the right sidebar choose Realm:
Then choose Swift and type the name of class as Task. Now you should see something like this:
Now the Task model class is ready for adding new properties.
Properties
Of course we need to add properties to the model class Task. Task will have name (String), createdAt (NSDate), notes (String), and isCompleted (Bool). The class will look like this after adding properties:
class Task: Object {
dynamic var name = ""
dynamic var createdAt = NSDate()
dynamic var notes = ""
dynamic var isCompleted = false
// Specify properties to ignore (Realm won't persist these)
// override static func ignoredProperties() -> [String] {
// return []
// }
}
We have added the properties of the Task model class and as you see all properties have been prefixed by dynamic var to make these properties accessors for the underlying database data.
Next, let’s create the TaskList model class, which is used to store the tasks:
class TaskList: Object {
dynamic var name = ""
dynamic var createdAt = NSDate()
let tasks = List()
// Specify properties to ignore (Realm won't persist these)
// override static func ignoredProperties() -> [String] {
// return []
// }
}
TaskList model class has name, createdAt and List
- The List<Object> is used for one-to-many relations as the TaskList has many tasks.
- List is very similar to Array for built in methods and accessing objects using indexed subscripting. List as you see is typed and all objects should be of the same type.
- List<T> is generic data type and that’s why we didn’t add dynamic before declaring tasks property because the generic properties can’t be represented in Objective-C runtime.
Creating relationships in Realm is pretty straightforward and as you see in previous implementations for the one-to-many relation. And as we said for one-to-one relation instead of using List<T>, we will use Object type. Consider this example:
class Person: Object{
dynamic var name = ""
}
class Car: Object{
dynamic var owner:Person?
}
In the previous example, the owner property acts as one-to-one relation to the Person data model.
Now the basic model classes have been created. I will continue to discuss with you about Realm by building a simple ToDo app. First download the app here and take a look. Run it on Xcode 7 (or up) and you will have screens like this:
In the project, I have added two view controllers: TasksViewController and TaskListViewController. The first controller is used for displaying a task item, while the second controller is used for showing all the available tasks. In the list view, click the + button to add a task list. Selecting the task list will proceed to the next screen. You can add multiple tasks there.
With a basic idea of the demo app, now let’s see how to add a new task list to the Realm database. To do that, you will need to handle a couple of things:
- Create new TaskList model object and save to Realm.
- Make a query to read the list from database to update the UI
To save objects in Realm, all you need to do is instantiate the subclass of the Object model, and then write the object to Realm. Here is a sample code snippet you need:
let taskListA = TaskList()
taskListA.name = "Wishlist"
let wish1 = Task()
wish1.name = "iPhone6s"
wish1.notes = "64 GB, Gold"
let wish2 = Task(value: ["name": "Game Console", "notes": "Playstation 4, 1 TB"])
let wish3 = Task(value: ["Car", NSDate(), "Auto R8", false])
taskListA.tasks.appendContentsOf([wish1, wish2, wish3])
We create a task list by instantiating a TaskList class and then set the properties of it. Then we create 3 objects (wish1, wish2 and wish3) of the type Task. Here I demonstrated 3 ways to create Realm objects:
- In wish1 object: Just instantiate the Realm class and set properties.
- In wish2 object: You can pass the properties in a dictionary with keys (properties names) and values.
- In wish3 object: You can pass the properties using arrays. The values in the array have to be in the same order as the corresponding properties in the class model declarations.
Nested Objects
Another feature in creating objects in Realm is nested objects. This feature can be used when you have one-to-one or one-to-many relations which mean you have properties of the type Object or List<Object>. In this case when you use the approach #2 or #3, you can replace the object with an array or a dictionary that represent its properties. Here is an example of nested objects:
let taskListB = TaskList(value: ["MoviesList", NSDate(), [["The Martian", NSDate(), "", false], ["The Maze Runner", NSDate(), "", true]]])
In the above code, we create a list of movies and set the name, createAt, and an array of tasks. Each task has been created using an array of its properties. For example [“The Maze Runner”, NSDate(), “”, true] represents a task with name, createAt, notes, and boolean of isCompleted or not.
Persisting Objects in Realm
Now you should know how to create Realm objects and use them, but in order to use them anytime and when app re-launched, you have to persist them in Realm database with write transactions. When you persist data to Realm, you can access these objects in any thread as long as they’ve been persisted to Realm. To perform a write transaction you need to have a Realm object. A Realm instance (also referred to as a realm) represents a Realm database. You can create the instance like this:
let uiRealm = try! Realm()
We use to add this line at the top of AppDelegate.swift in order to share the object across all Swift files. Later you can easily call the write method like this:
uiRealm.write { () -> Void in
uiRealm.add([taskListA, taskListB])
}
First the uiRealm object is created in the AppDelegate class, and will be shared over whole app to be used. The Realm object should be created only once per thread because it’s not thread safe and can’t be shared between threads. If you wanna perform write operation in other thread, you have to create a new Realm object. I have named it as “uiRealm” as this instance is specified for the UI thread.
Now let’s go back to our app, we save the task lists after a user clicks the Create button. In the displayAlertToAddTask method of TasksViewController, we have a createAction object like this:
let createAction = UIAlertAction(title: doneTitle, style: UIAlertActionStyle.Default) { (action) -> Void in
let taskName = alertController.textFields?.first?.text
if updatedTask != nil{
// update mode
uiRealm.write({ () -> Void in
updatedTask.name = taskName!
self.readTasksAndUpateUI()
})
}
else{
let newTask = Task()
newTask.name = taskName!
uiRealm.write({ () -> Void in
self.selectedList.tasks.append(newTask)
self.readTasksAndUpateUI()
})
}
}
In the above code, we get the task name from the text field, and call the write method of Realm to save the task list.
Please note that when you perform multiple writes simultaneously, they will block each other and block the thread they working on. So you should consider doing this in separate thread than the UI. Another thing is that, reads are not blocked while you’re performing write transactions. It’s helpful as your app may perform many reads operation while user navigating the app and in same time write transactions may be working in background.
Retrieving Objects
Now that you’ve learned how to write data in Realm, but it’s useless without knowing how to retrieve them back! Querying a Realm database is very straightforward. You’re provided with a lot of options to customize and filter your data. When you perform a query in Realm, it returns a list of Results object. Simply think of Results as a Swift array because it has an interface very similar to that of an array.
When you have a Results instance, you receive data directly from disk. Any modification to the data (within the transactions) will affect the data on disk directly. In Realm to query objects, you just call the objects method with a class name as parameter. Let’s see how to use this to read TaskLists and update the UI:
We have defined this property in TasksListsViewController:
var lists : Results!
And implement the method readTasksAndUpdateUI like this:
func readTasksAndUpdateUI(){
lists = uiRealm.objects(TaskList)
self.taskListsTableView.setEditing(false, animated: true)
self.taskListsTableView.reloadData()
}
And in the tableView(_:cellForRowAtIndexPath:_) method, we display the list name and number of tasks inside this list:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCellWithIdentifier("listCell")
let list = lists[indexPath.row]
cell?.textLabel?.text = list.name
cell?.detailTextLabel?.text = "\(list.tasks.count) Tasks"
return cell!
}
Pretty straightforward, isn’t it?. The last thing is to call the readTasksAndUpdateUI function in viewWillAppear, to make sure that this view is always updated when it’s opened.
override func viewWillAppear(animated: Bool) {
readTasksAndUpdateUI()
}
This is how we read and write the task lists using Realm. Next up, we will see now how to perform update/delete operations in Realm. Before starting, let’s go through some of the code in the project template for the edit/delete operations of lists.
First we have a boolean in TaskListsViewController called isEditingMode, which is used for switching between the editing and normal mode:
var isEditingMode = false
When the Edit button is tapped, the didClickOnEditButton method will be called:
@IBAction func didClickOnEditButton(sender: UIBarButtonItem) {
isEditingMode = !isEditingMode
self.taskListsTableView.setEditing(isEditingMode, animated: true)
}
This action will enable/disable the editing mode of UITableView by using method setEditing in table view. In table view, the default action in editing mode in cells is the delete action but starting from iOS 8.0, a new method called editActionsForRowAtIndexPath is added in UITableViewDelegate to customize the actions that will appear in cells when the user swipes the cell.
We implement this method to add two actions delete and edit like this:
func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {
let deleteAction = UITableViewRowAction(style: UITableViewRowActionStyle.Destructive, title: "Delete") { (deleteAction, indexPath) -> Void in
//Deletion will go here
let listToBeDeleted = self.lists[indexPath.row]
uiRealm.write({ () -> Void in
uiRealm.delete(listToBeDeleted)
self.readTasksAndUpdateUI()
})
}
let editAction = UITableViewRowAction(style: UITableViewRowActionStyle.Normal, title: "Edit") { (editAction, indexPath) -> Void in
// Editing will go here
let listToBeUpdated = self.lists[indexPath.row]
self.displayAlertToAddTaskList(listToBeUpdated)
}
return [deleteAction, editAction]
}
Here we have added two actions using UITableViewRowAction which takes the action style, title, and handler to handle the action. Now the actions will look like this when you swipe the cell or when you click on destructive button in edit mode:
This is how the app’s UI works for deletion and update.
Deleting Objects
To delete objects/data from Realm database, you can just call the delete method of a Realm object by passing the object to delete. And of course it should be done in a write transaction. Take a look at the following code, which is how we delete a task list from the Realm database:
let listToBeDeleted = self.lists[indexPath.row]
uiRealm.write({ () -> Void in
uiRealm.delete(listToBeDeleted)
self.readTasksAndUpdateUI()
})
After deletion we called readTasksAndUpdateUI function to query and update the UI.
Instead of deleting a single object, there is another function called deleteAll in Realm, which allows you to delete all data for all classes in the database. This method is very helpful when you persist data for current user and you want to clear database when he logs out.
uiRealm.write({ () -> Void in
uiRealm.deleteAll()
})
Updating Objects
In Realm there are multiple ways to update objects but all of them should be accomplished inside a write transaction. Below we will take a look at some of the approaches for updating objects.
Using properties
You can update any Realm object by just set the property with the new value inside a write block. For example, in TasksViewController, we simple change the task’s status by setting the property:
uiRealm.write({ () -> Void in
task.isCompleted = true
})
Using Primary Keys
Realm supports marking a single string or int property on an object as the primary key. When creating a Realm object using add(), if the key already exists, the object will be updated with the new values. Here is an example:
let user = User()
user.firstName = "John"
user.lastName = "Smith"
user.email = "[email protected]"
user.id = 1
// Updating User with id = 1
realm.write {
realm.add(user, update: true)
}
The id is set as the primary key. If the user with id 1 exists, Realm will update the object accordingly. Otherwise, Realm will insert the object into the database.
Using KVC (Key-Value Coding)
If you are an experienced iOS developer, you must be familiar with key-value coding. Realm classes like Object, Results, and List, are KVC compliant. This helps you set/update properties in runtime. Another great feature of conforming KVC in List and Results, is that you can update objects of a collection in bulk without the need of iterating over each object and update it. I know you may not understand it thoroughly. Let’s see this example:
let tasks = uiRealm.objects(Task)
uiRealm.write { () -> Void in
tasks.setValue(true, forKeyPath: "isCompleted")
}
In the above code, I made a query to get all Task objects, and set isCompleted to true for all the returned objects. This means I marked all tasks in the Realm database as completed by using just one line of code.
Let’s get back to our ToDo app. If you look into the displayAlertToAddTaskList method again, you should find the following code snippet:
// update mode
uiRealm.write({ () -> Void in
updatedList.name = listName!
self.readTasksAndUpdateUI()
})
It is executed when a user edits the list name. We simply update the list name by setting the name property.
Displaying Tasks
I’ve walked you through most of the code in TaskListViewController. Now let’s take a look at TasksViewController that is used to display the task item in a task list. The view controller has a UITableView that is splitted into two sections: completed and open tasks. In TasksViewController, we have these properties:
var selectedList : TaskList!
var openTasks : Results!
var completedTasks : Results!
selectedList is used to hold the selected task list, passed by TaskListsViewController. To filter the tasks into open and completed statuses, we declare two variables: openTasks and completedTask. For filtering, we are going to use Realm’s magic function filter(). Let’s see how it looks like in code before I explain to you how the filtering works:
func readTasksAndUpateUI(){
completedTasks = self.selectedList.tasks.filter("isCompleted = true")
openTasks = self.selectedList.tasks.filter("isCompleted = false")
self.tasksTableView.reloadData()
}
In the method, we call the filter method to filter results. Realm provides an easy way to filter your query using the method filter(). The method can be called by instances of List, Result and Object. It returns the specific objects based on condition set in the filter string. You can think of filter as NSPredicate. Basically you can say they are totally the same. You can create NSPredicate with a predicate string and pass it to filter method like what we did in filtering the completed tasks.
Let’s take a look at another example:
// using predicate string
var redCars = realm.objects(Car).filter("color = 'red' AND name BEGINSWITH 'BMW'")
// using NSPredicate
let aPredicate = NSPredicate(format: "color = %@ AND name BEGINSWITH %@", "red", "BMW")
redCars = realm.objects(Car).filter(aPredicate)
In the above code, we filter those cars in red and have a name started with “BMW”. The first line of code just uses the filter string to perform the filtering. Alternatively, you can use NSPredicate to achieve the same results. Here is a table that summarizes most common operators in filter comparisons:
Sorting
Now that we have talked about the basic operations of Realm database, there is another feature I want to introduce to you before the end of this tutorial. Sorting is another useful feature provided by Realm. In List and Result, you can call the method sorted(“sore criteria”) to sort collection of data. Let’s see how to use this to sort tasks list alphabetically or by date creation. First in UI we will add segmented control to sort the lists based on selection:
And to perform sorting based on selection, logic will go like this:
@IBAction func didSelectSortCriteria(sender: UISegmentedControl) {
if sender.selectedSegmentIndex == 0{
// A-Z
self.lists = self.lists.sorted("name")
}
else{
// date
self.lists = self.lists.sorted("createdAt", ascending:false)
}
self.taskListsTableView.reloadData()
}
Conclusion
Realm is very easy and straightforward solution to manage local storage and databases. Realm gives you the power of scalability and simplicity in just couple of lines of code. For most of apps and even games I think you are in need of using database and I think it worth to give it a shot.
Where to go from here?
In this tutorial you are capable of using Realm in your project and do all basic operations like Reading, Writing, Updating, Deletion. Realm still has some advanced topics and some stuff that worth to check them out. The best reference I would recommend is the official site of Realm, really these guys did awesome work for documenting everything.
For full source code for the Todo app, you can simply check it out here.
Feel free to write comment when you have a problem in the implementation and we would be more than happy to help you.