This is the second post in a multi-part series on the Realm Mobile Database; it highlights some of the foundation classes that make using the database a breeze in any of the star programming languages we support. If you missed the first post we recommend taking few minutes to read through. Also make sure to check out the third post on sharing code across platforms, the fourth post highlighting how a flexible, solid SDK like Realm’s empowers good development practices., the fifth post on Realm SDK features that allow developers to provide partial UI updates based on fine-grained notifications, and the final and sixth post, which shows how the features in the previous parts come together to make a database for modern mobile apps.
Since the Realm Mobile Database was specifically designed to be used on mobile devices, it’s built from the ground up to be efficient.
We’ve already discussed few aspects in the previous post about why the Realm Mobile Database performs so well:
- it doesn’t use a third party storage engine,
- it doesn’t use intermediate query language like SQL,
- and it doesn’t need to continuously convert data back and forth as it reads or writes to the disk.
As we mentioned earlier, there are two somewhat different parts to the Realm Mobile Database: the C++ core and the SDKs that you use, written in the language you’re using to build your app.
So what does the Realm Mobile Database do in its SDKs to help you have more performant and robust code? In this article, we are going to have a look at some common patterns that do so.
Who Needs a Fetched Results Controller?
A common pattern in mobile development is displaying long lists of items, be it the user’s contacts, entries in a calendar, or list of tweets.
When using a “traditional” storage, you would often just copy items from disk into an array in memory, effectively duplicating your model — and if you’re not careful with copying arrays, you might just triple it as well.
If you’re dealing with very long lists, you’d like to be more lean than just loading all data into memory. You need to write code to load pages (or batches) of records from disk (preferably in the background to keep UI scrolling smooth) and copy only the current batch into memory. Then you have to keep track of how far the user has scrolled the list and load more batches as they scroll further.
A common solution to this issue would be using NSFetchedResultsController
(on Apple platforms) or similar. But remember: this is a pre-fetching class that runs on top of an ORM that runs on top of an SQLite database. And that feels like a lot of middlemen right there between you and your data:
In contrast, with the Realm Mobile Database you use a class called Results
, which helps you directly query objects on disk and load only the ones you actually are using at a given moment into memory.
Let’s look at some of the benefits.
First and foremost, defining an object query does not load any objects from disk. For example:
realm.objects(Repository.self)
.filter("name contains[c] %@", searchTerm ?? "")
.sorted(byProperty: "stars", ascending: false)
This code defines a kind of viewport to the database — you’re interested in Repository
objects that have a name containing a given search term, and you’re interested in them in the order of their amount of stars.
You don’t need to execute or run in another way that object query. Realm will start loading data from the disk as you actually start iterating over the objects in that result set.
Results
allows you to only load the data you actually need and use. (Within reason of course - you can’t realistically read isolated bytes from disk)
So when you chose to drive a UI table view with the Results
class, you read from disk only the data to display the currently visible table rows.
The best part? You don’t need to do anything extra to achieve that. While your user scrolls through a table view with hundreds of thousands of rows, Results
keeps in memory only the data needed to display the 5-6 rows you have on screen at a time.
Dealing with Lists? We Got This.
Let’s look at another class from the Realm Mobile Database’s API - List
. The List
class allows you to have a one-to-many relation between objects. For example:
class User: Object {
dynamic var name: String = ""
}
class Repository: Object {
dynamic var repository: String = ""
let contributors = List<User>()
}
You are probably familiar with this kind of relationship from SQL databases. In your SQL database, you need to define an extra table that sits between the two tables - say users
and repositories
. Anytime you want to get the contributors for a given repository - you need to search through all relationships between all repositories and all users in your database to find the ones that you’re interested in.
Soon enough you end up with something like this (and you’ve seen this kind of tangled relationships if you’ve worked on complex SQL databases):
So if the repository EasyAnimation
has two contributors, to get the details of these two users you need to search through all links between the records in users
and repositories
. Depending on how many repositories and users you have, the relationship index might be hundreds, thousands, or millions of records. And you have to work through those to find out the two records you’re actually interested in, every time you want to fetch them.
As you can imagine, this isn’t very performant. That’s why in teams working on very large databases you need to get permission from the team lead to use a JOIN
clause in a database query (speaking from experience here).
In contrast, the Realm Mobile Database does not join tables and does not use SQL queries. A Realm Mobile Database is pretty much just objects, like you have them in memory. When one object points to another, the parent doesn’t typically copy the child’s data, right? Of course not, it just keeps a pointer to it.
That’s why getting the two contributors to a Repository
object with the Realm Mobile Database means just keeping two direct pointers to two other objects, which you can use anytime you want. No lookups, no relationships, no indexes.
List
is really good at keeping one-to-many relationships. It works largely like an Array
: it preserves the order in which objects were added to it, and it gives you methods to append, remove, or move objects to a certain index. And again, it costs almost nothing to create a List
: that object itself only stores a list of direct indexes to other objects, and as you probably know indexes are cheap to store.
That being said, using List
in creative ways can add a lot of performance to your app.
Let’s consider the following case: You have a Repository
object that keeps a list of contributors, and some of those contributors are awaiting approval and access.
You want to display two lists in your app’s UI: one of the pending contributors and the other with the approved ones. You might be tempted to approach this in the way you’re used to working with other databases and write:
// this is the slow old way
class User: Object {
dynamic var name: String = ""
dynamic var isPending: Bool = true
dynamic var dateAdded: NSDate = NSDate()
}
class Repository: Object {
dynamic var repository: String = ""
let contributors = List<User>()
}
//display pending users
realm.objects(Repository.self).first!
.contributors
.filter("isPending = true")
.sort(byProperty: "dateAdded", ascending: false)
//display approved users
realm.objects(Repository.self).first!
.contributors
.filter("isPending = false")
.sort(byProperty: "dateAdded", ascending: false)
Again - there’s no need to go this way with the Realm Mobile Database.
This would be a valid approach if you were using an SQL database. It would be the most you could do because you would need to filter the same tables over and over again. However, think about this one more time: Why do you need to filter and sort those object every single time you want to show them on screen?
List
to the rescue! How about keeping two separate lists: one of the pending contributors and another with the approved ones? How would that help you ask?
You will never have to filter or sort the users again (and frankly these two operations are what is slowing down your queries).
In a list, you will essentially iterate over the precise objects you need in the precise order you want! In a sense, you pre-filter and pre-sort the objects in each of the two lists, thus doing the work only once. Then you simply iterate over the collection when you need to display the data.
Let’s have a look at that more performant setup. If the dateAdded
property was used only for sorting it be removed safely. isPending
can stay but it won’t be used for filtering - only to determine the status of a single User
object when needed.
// the easy Realm way
class User: Object {
dynamic var name: String = ""
dynamic var isPending: Bool = true
}
class Repository: Object {
dynamic var repository: String = ""
let approvedContributors = List<User>()
let pendingContributors = List<User>()
}
And of course you don’t need to query the contributors anymore, you can just iterate over the two lists. When you add a new User
object, add it to pendingContributors
; when the user is approved, remove the User
from pendingContributors
and add it to approvedContributors
.
// user requesting repo access
let user = realm.object(ofType: User.self, forPrimaryKey: id)
try! realm.write {
repo.pendingContributors.append(user)
}
//later, when the user is approved
try! realm.write {
user.isPending = false
if let index = pendingContributors.index(of: user) {
pendingContributors.remove(objectAtIndex: index)
}
approvedContributors.append(user)
}
You can have further lists added to the same Repository
object, since they’re not expensive to create or maintain.
Finally, since lists only keep direct links to existing objects, you can have the same object added to multiple lists. You will not take any performance hit for doing so.
For example the same User
object might be both in the list of approved users and in the history list of commits to the repository object. Each of these lists can drive a different screen of your app:
Now that you have a better understanding of Realm’s lists - compare one last time what it requires to show a repository’s commits on screen with SQL tables compared to object lists.
Using the former, each time the user opens the commit list screen you will need to go over the table of all commits, filter all records to only the current repository and match the users table against the results, and finally sort all those records.
When using a List
, you will just read the first few objects from the Repository
’s commits list and display them on screen. Whoa! 👌
The List
class is designed to power item lists, and those are everywhere in mobile UI. You can get the most out of this class if you step away from how you’re used to using other storages and think about your data in terms of objects and object graphs.
Last but not least - we’ve been using the Swift Realm SDK in this article, but things work exactly the same way on other platforms as well. Building efficient object queries and object lists are implemented in the Realm Mobile Database Core, so they work the same way across platforms and programming languages.
In the next posts in this series, we’ll have a look at more classes provided by the Realm Mobile Database SDK and how they aid quick and efficient mobile development.
We Hope You Like What You’re Seeing!
We’ve just scraped the surface of the many ways the Realm Mobile Database has been designed to solve present day apps’ challenges.
In fact, if you have few extra minutes, you can read more about the Realm Mobile Database:
- Download the database (it’s free!)
- Get started with easy Swift sample code with this video course
In the next installment of this series, we’ll take a look at the ways the Realm Mobile Database’s API empowers clean software architecture and modern best practices.
We’re going to leverage that modular code and explore a multi-platform project, which shares code between tvOS, iOS, macOS, and watchOS in the same project!
See you next time! 👋
Receive news and updates from Realm straight to your inbox