Getting Started With Realm Database In Swift

Written by Reinder de Vries on February 20 2019 in App Development

Getting Started With Realm Database In Swift

Realm is an easy-to-use database framework for iOS and Swift. In this article, you’ll learn how to get started with Realm Database in your own iOS app projects.

We’ll discuss how Realm works, how you can use it in your iOS projects to save and retrieve data, and how Realm is different from the more conventional database tools for iOS.

Realm is a powerhouse when it comes to managing databases, and as you’ll see, it’s convenient to use together with Swift. It’s an especially fine choice for local-only databases, although the Realm Platform has everything you might need from a synchronised, cloud-based service.

Can’t wait to get started? Let’s begin!

  1. What’s Realm?
  2. Creating Realm Data Models
  3. Saving Data In Realm Database
  4. Getting Data From Realm Database
  5. Further Reading

What’s Realm?

Before we write some Swift code, let’s discuss what Realm Database is. Realm is a database library (and platform) for iOS, Android and the web. It’s a compelling alternative to SQLite and Core Data – tools that are commonly used in practical iOS development.

Realm has three distinct products:

  • Realm Database is a local database framework (open source)
  • Realm Platform is an offline-first cloud database service ($30/m)
  • Realm Studio is a management tool for Realm Database and Realm Platform

This article focuses solely on the open source Realm Database, although the code discussed here can also be used with Realm Platform.

Realm is a database, so you can use it to store and retrieve data objects in your iOS app, in a structured manner. The database is persisted between app launches, so it’s perfect for long-term object storage.

A few examples:

  • You can use Realm to cache news article data, so your app’s user can read the latest news offline and on-the-go
  • You can use it to store save game data in your iOS game app, and load the data so a user can continue gaming where they left off
  • You can use Realm to store text notes in a database, and their properties, such as the note’s title and when it was created

It’s easiest to compare a database to a spreadsheet, like the one below. The rows of the spreadsheet correspond to individual entries in the database, called objects, and the columns of the spreadsheet describe properties of those objects.

Database Spreadsheet Example

We can describe the above structure with a Swift class, like this:

class User {
    var ID:Int
    var username:String
    var highscore:Int
}

A database can contain many instances of the above User class, just like the aforementioned spreadsheet has four data entries.

What’s so interesting and convenient about Realm is that you can work with database objects as actual Swift objects. Unlike Core Data, SQLite and Firebase, you don’t have “map” objects or rows returned from the database to Swift objects. Realm does this for you – you can just drop a Swift object in a Realm database, and when you query objects, you get that same class right back.

Installing Realm in your project is straightforward. For the complete instructions, please check out the Realm Documentation. Here’s the short version:

  1. Install CocoaPods on your Mac with sudo gem install cocoapods
  2. Run pod repo update to get the latest specs from CocoaPods
  3. Add a Podfile to your project, and add pod 'RealmSwift'
  4. In Terminal, go to your project directory and run pod install
  5. Open the .xcworkspace file in Xcode to continue working on your project

Now, let’s write some actual Swift code!

Learn how to build iOS apps

Get started with iOS 13 and Swift 5

Sign up for our iOS development course Zero to App Store and learn how to build professional iOS 13 apps with Swift 5 and Xcode 11.

Creating Realm Data Models

Realm data models are nothing more than regular Swift classes, with regular properties. In our hypothetical app, we’re going to save notes, with the following class:

class Note {
    var id = 0
    var title = ""
    var created = Date()
    var text = ""
}

The above class defines 4 properties. Every property has a default value, and the types of these properties are inferred by Swift (Int, String, Date, String respectively).

Here’s how you’d create an instance of the Note class:

let note = Note()
note.id = 1
note.title = "Shopping list"
note.text = "Milk, butter, cake, apples"

This Note class can’t be used with Realm rightaway, so we’ll have to change it a bit. Like this:

class Note: Object {
    @objc dynamic var id = 0
    @objc dynamic var title = ""
    @objc dynamic var created = Date()
    @objc dynamic var text = ""
}

Here’s what’s changed:

  • The Note class now subclasses the Object class, which is part of the RealmSwift library. This subclass contains the code needed to work with Realm.
  • Every property that we want to save in Realm needs to get those @objc and dynamic attributes, right before var. This ensures that the properties can be accessed by the underlying Realm framework.

Whenever you work directly with the Realm library’s classes, don’t forget to import RealmSwift at the top of your Swift file!

In most database environments, you also want to uniquely identify objects. How do you know if two Note instances refer to the same underlying data? In our hypothetical Note class, we’ll use the property id to give each note a unique identifier. When the id property of two Note objects are the same, they’ll refer to the same Realm database object. That way we can update, change or overwrite them.

Realm needs to know which property we want to use as a unique identifier. We could use id, but also title, and even the note’s creation date. Here’s how we tell this to Realm:

class Note: Object {
    ...

    override static func primaryKey() -> String? {
        return "id"
    }    
}

The primaryKey() function is added to the Note class, and overridden from its superclass implementation. The only thing we do inside the function is return a string "id", which contains the name of the property that uniquely identifies a Realm data object.

OK, now that the Note data model is in place – we can finally store and retrieve stuff in Realm!

Data models in Realm can get much more complex than this, with optionals, arrays with List, custom getters and setters, indices, ignored properties, and complex data types. Most of that is configured right in your data model! You can read more about that in the Realm Documentation.

Saving Data In Realm Database

Writing data to a Realm database is straightforward:

  1. Get a reference to a Realm instance
  2. Add the objects you want to save to a write transaction
  3. Commit the write transaction to the database

Let’s discuss the principles implied here. We’ve already discussed data models in the previous section. The Realm environment has another important component, the Realm itself.

Technically, a Realm is an instance of a Realm Mobile Database container. In reality, it’s a .realm file that stores database objects. It’s easiest to regard a Realm as “the database”.

An app has a default Realm, and you can create additional Realms as you see fit. Realms can also synchronize with the cloud-based Realm Platform. We are just going to use the default, local Realm.

Before you can read from or write to the database, you’ll need to get a reference to a Realm instance. Here’s how:

try {
    let realm = try Realm()
} catch {
    print(error.localizedDescription)
}

In the above snippet, the let realm = try Realm() code instantiates the default Realm and assigns it to the realm constant. We also use Swift error handling to catch any errors that might arise.

It’s worth noting here that …

  • … instantiating a Realm can throw errors if resources are constrained, ex. the iPhone has run out of memory or storage
  • … at runtime, only the first Realm() instantiation will throw errors, if any, because subsequent access to that Realm is cached and will always succeed
  • … so, it’s important you design your app in such a way you can recover from errors thrown from any initial Realm() calll, while also getting errors out of your way with try! or try?

In Realm, any change to the database must be done in a so-called write transaction. This includes newly created or added objects, updates to objects, and deletions.

A write transaction batches all changes to the database, so they can be written sequentially to Realm in one go. Writing to the database is a blocking, synchronized operation, so it’s more efficient to make all changes at once. You should also aim to minimize the number of write transactions, i.e. to batch as much as possible.

Let’s say that your app user has created a note in the app, and wants to save it. We define the following note object:

let note = Note()
note.id = 2
note.title = "Remember the milk!"
note.text = "Mom asked if I could bring a gallon of milk home."

Just like before, we get a reference to the default Realm:

let realm = try! Realm()

Finally, we add the note object to Realm in a write transaction:

try! realm.write {
    realm.add(note)
}

Here’s what happens:

  • The add(...) function call is wrapped in a realm.write { ... } block. This block encapsulates the write transaction, i.e. anything that’s added, changed or deleted within this block is written to Realm as part of the transaction. (Note that it’s technically a trailing closure!)
  • The try! statement disables error handling for convenience, but you should always follow a sensible approach to handle errors. When the write transaction fails due to errors, and you’ve used try!, your app will crash.
  • The realm.add(note) code adds the note object to Realm. This object is now saved in the Realm Database, and we can later get it back from the database (see next section).

What if you want to add multiple objects to Realm? You can do all of that within the write { ... } block, but in some scenarios that’s impossible.

Check this out:

realm.beginWrite()

for text in notes
{
    let note = Note()
    note.text = text

    realm.add(note)
}

try! realm.commitWrite()

In the above code we’re iterating over an array of strings called notes, adding each of the strings to a Note object, and saving that object in Realm.

We can’t use a write { ... } block here, so that’s why we open the write transaction with realm.beginWrite(). Every object is subsequently added, and once that’s done we commit the transaction with realm.commitWrite().

Working with a write transaction this way effectively “opens the gates” for the new objects, then marks the transaction as complete, after which the data is written to the database.

Keep in mind that opening and closing the transaction needs to be balanced, i.e. you shouldn’t call beginWrite() without also calling commitWrite() at some point.

Updating and deleting objects works the same way. Consider that we want to change the text property of a previously saved note object, and save that change in the database. Here’s how:

try! realm.write {
    note.text = "Mom asked, bring two gallons of milk!"
}

Likewise, objects can also be deleted from the Realm database:

try! realm.write {
    realm.delete(note)
}

Awesome! Let’s move on, and discuss how you can retrieve objects from Realm.

Getting Data From Realm Database

Imagine that your app’s user has created 10 notes in the app. All of these notes are stored in the Realm database. You want to display these notes in a table view. How do you retrieve the Note objects from Realm?

Retrieving data from Realm is done with a query. A query essentially encapsulates the information that’s needed to retrieve objects. A few examples:

  • Get all Note objects
  • Get the notes from 7 days ago, up to now
  • Get the notes that include the word “groceries”

To get all notes from Realm, we simply do this:

let notes = realm.objects(Note.self)

This instructs Realm to retrieve all objects of type Note, and assign them to the notes collection. We can then iterate the notes collection to display all the notes’ titles.

Iterating over the collection is as simple as:

for note in notes
{
    print(note.text)
}

A few things are important to note here. First, the type of notes is Results. This is a generic collection type from Realm. It functions similarly to an array, i.e. you can iterate the collection’s items, but there’s more:

  • A Results collection directly references the data in the database. The data isn’t copied, and changes to the data (in a write transaction) are reflected back to the database immediately.
  • The Results collection is auto-updating, so when some other part of your app (same thread) changes the data in that same result set, you see those changes reflected immediately. This avoids a ton of conflicts.
  • The collection items in Results are lazy, i.e. if you query thousands of objects, those objects are only loaded if and when they’re accessed. Queries are also deferred; they’re executed when needed, so you can safely chain together complex queries without worrying about intermediate execution.

Querying Realm objects and working with Results is powerful, and it gets more complex from here on out. A few other things Realm can do are:

  • Filtering result sets with the filter(_:) function. This works the same as NSPredicate, so you can filter and query objects based on a wide range of parameters.
  • Sorting result sets is done with the sorted(_:) and sorted(byKeyPath:ascending:) functions. You can even sort by multiple properties using SortDescriptor objects.
  • You can “chain” queries with little overhead. This allows you to first query, for example, notes from a given time period, and then select notes that include a particular search phrase.
  • Unlike SQL-based queries, you don’t have to limit queries in Realm. Because results are lazy, you only retrieve data you actually access. This means you don’t have to paginate large results sets, and that you don’t have to do anything extra to create infinite scrolling with UITableView.
  • Realm can do relationships, of course! The framework is fully relational, so you can create one-to-one, many-to-one and many-to-many relationships between objects. And what’s really cool is that you can directly reference Swift objects in relationships, and traverse List collections with ease.
  • Realm supports migration operations, i.e. upgrading the database of an existing app at runtime. You can also bundle .realm databases with your app, to include a database filled with actual data, as a user downloads your app.

Learn how to build iOS apps

Get started with iOS 13 and Swift 5

Sign up for our iOS development course Zero to App Store and learn how to build professional iOS 13 apps with Swift 5 and Xcode 11.

Further Reading

We’ve only barely scratched the surface of what Realm can do – it can do much, much more. Especially when you consider their cloud-based Realm Platform, and Realms offline-first synchronization approach.

What makes Realm so compelling is that there’s little syntactical overhead in dealing with the database itself. Objects you store in the database are just Swift class instances, and what you get back are those same objects. Neat!

Want to learn more? Check out these resources:

Reinder de Vries

Reinder de Vries

Reinder de Vries is a professional iOS developer. He teaches app developers how to build their own apps at LearnAppMaking.com. Since 2009 he has developed a few dozen apps for iOS, worked for global brands and lead development at several startups. When he’s not coding, he enjoys strong espresso and traveling.