Welcome back! Best wishes for a safe and enjoyable upcoming holiday!

As an iOS developer you’ll run into situations that require maintaining local data to help execute necessary tasks or improve user experience (especially when your app has no network connection). Almost every app you touch is being powered by a cloud solution that leans on a database to manage sets of data. If you were to look at the structure of an existing database you’ll see an intricate structure built with many relationships between sets of data. Even small apps take advantage of databases. The first app I built used SQLite to manage drink recipes that individuals could create, edit, or delete. As I gained experience I moved to Core Data seeking something a little cleaner and easier to use.

Today we’re looking at a contender that is specifically designed for mobile devices. Realm is a mobile database that replaces SQLite and Core Data. It’s been under development since 2011 and, from what I’ve experienced, it’s extremely clean, easy to understand, and powerful. Alternatives to Realm require a lot of code which increases complexity and involves a lot of time to get right. Not only that, but thread safety is a big challenge that takes a lot of time to really understand. Realm takes care of that for you under the hood while making other tasks insanely easy to complete. Trust me when I say that chasing Core Data issues in a multithreaded environment is not fun at all.

Now, it’s important to know that Realm is not built on top of SQLite. If you’re a database veteran that may inflict so much pain that you’re now moving to close this article. The guys and gals at Realm have created a custom C++ core that makes the efficiencies possible. They wanted something new and fresh that is easier to use while maintaining a high level of excellence.

Let’s Explore

In this example we’re going to focus on introducing the fundamentals of Realm. I’m not building a complex database. Instead, I’m exploring how we lay a foundation of understanding.

This example simulates fetching new movies from Rotten Tomatos. When you’re experimenting it’s good to simulate as much as you can. Instead of wrestling with bugs in the APIs and secret keys we can fake the request and work from there to create our structure. This is actually how I build new apps today. Instead of worrying about building the interface and connecting it with a cloud backend immediately, I just focus on the interface using simulated data.

Downloading Realm Required

When you go to add Realm to your project you’ll need to download Realm if you’re using Swift. You can get that over at Realm.io. The reason for that is CocoaPods does not work well with Swift files yet. No biggie, the Realm team have provided a very simple set of steps to get things running. Review the manual installation instructions under getting started (video included).

XMCMovie Model Object

This object is our first Realm model object. When you create a new object you’ll subclass RLMObject and outline the necessary properties you’re looking for. In my case I wanted to store the movie title, rating, reviewer consensus, and the name to the poster art.

import UIKit import Realm class XMCMovie: RLMObject { dynamic var id = "" dynamic var title = "" dynamic var tomatometer = 0 dynamic var consensus = "" dynamic var imageName = "" override class func primaryKey() -> String! { return "id" } override init() { super.init() } init(id: NSString, title: NSString, tomatometer: Int, consensus: NSString, imageName: NSString) { super.init() self.id = id self.title = title self.tomatometer = tomatometer self.consensus = consensus self.imageName = imageName } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import UIKit import Realm class XMCMovie : RLMObject { dynamic var id = "" dynamic var title = "" dynamic var tomatometer = 0 dynamic var consensus = "" dynamic var imageName = "" override class func primaryKey ( ) -> String ! { return "id" } override init ( ) { super . init ( ) } init ( id : NSString , title : NSString , tomatometer : Int , consensus : NSString , imageName : NSString ) { super . init ( ) self . id = id self . title = title self . tomatometer = tomatometer self . consensus = consensus self . imageName = imageName } }

The really interesting bit to this object is the function primaryKey(). This is used by Realm to help you update existing records instead of creating possible duplicates. Doing it this way means that Realm can used optimized look up capabilities while you avoid complicating your own code base with validation checks.

One thing that I’m not doing in this example is creating relationships with other model objects. This is something you’ll most certainly do in the future so it’s good to be aware of. Below you’ll find a challenge that involves relationships. Explore the relationship section over at Realm if you get stuck.

// Say we have a Director model object. We could say that this movie belongs to only 1 Director dynamic var director: Director // On the flip side, movies usually have several writers. Imagine having a Writer model object dynamic var writers = RLMArray(objectClassName: Writer.className()) 1 2 3 4 5 // Say we have a Director model object. We could say that this movie belongs to only 1 Director dynamic var director : Director // On the flip side, movies usually have several writers. Imagine having a Writer model object dynamic var writers = RLMArray ( objectClassName : Writer . className ( ) )

Saving Data

Remember that in our example we’re using simulated data. In XMCApi.swift we provide a function requestOpeningMovies() which will create several XMCMovie objects for us and save+update it within the Realm database. Since the ratings of our movies can change it’s important to update our local data model instead of creating brand new objects.

// Write our movie objects to the database let realm = RLMRealm.defaultRealm() realm.beginWriteTransaction() for movie in movies { /* This method will avoid duplicating records by looking at the primary key we've set on our object. Go look at the XMCMovie class to see that method defined. */ XMCMovie.createOrUpdateInDefaultRealmWithObject(movie) // Alternatively, you could add new objects by calling this method // realm.addObject(movie) // or // realm.addObjects(movies) // An array of objects } realm.commitWriteTransaction() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 // Write our movie objects to the database let realm = RLMRealm.defaultRealm() realm.beginWriteTransaction() for movie in movies { /* This method will avoid duplicating records by looking at the primary key we've set on our object. Go look at the XMCMovie class to see that method defined. */ XMCMovie . createOrUpdateInDefaultRealmWithObject ( movie ) // Alternatively, you could add new objects by calling this method // realm.addObject(movie) // or // realm.addObjects(movies) // An array of objects } realm . commitWriteTransaction ( )

Proving It Works

It’s always good to see how thing come together in a database. Realm includes a browser that lets you review the contents of your database file. Navigate to the realm download folder and you should see a browser folder that contains the app.

Now where is the database file stored? It’s worth adding a log statement somewhere in your app that prints the path for you. You’ll just want to call the following method to know where to grab it.

// Easily see where the realm database is located so we can open it with the Realm Browser println(RLMRealm.defaultRealm().path) 1 2 // Easily see where the realm database is located so we can open it with the Realm Browser println ( RLMRealm . defaultRealm ( ) . path )

Loving NSFetchedResultsController

If you’re a fan of Core Data you may hesitate to use Realm because of how convenient NSFetchedResultsController can be. Well, rest easy in knowing that Realm allows you to easily subscribe to a notification that is fired every time a write transaction is committed. The notification remains active as long as you hold onto a reference. This is a great time to reload a collection view, table, or update other interface components.

let realm = RLMRealm.defaultRealm() let token = realm.addNotificationBlock { note, realm in // Update your user interface, reload your table, rest easy } 1 2 3 4 let realm = RLMRealm . defaultRealm ( ) let token = realm . addNotificationBlock { note , realm in // Update your user interface, reload your table, rest easy }

Query All The Data

We’re finally at the point where we should use the data we’ve saved. In this example we make the request for the movie data and execute a query for the movies that rated under 60%. You can review the database in the Realm browser and press the next button to verify that the correct results were returned.

var movies = [XMCMovie]() var index = 0 override func viewDidLoad() { super.viewDidLoad() // Easily see where the realm database is located so we can open it with the Realm Browser println(RLMRealm.defaultRealm().path) // Simulate grabbing data from a web service & creating XMCMovie objects XMCApi.requestOpeningMovies() // This query will grab all of our movies // let allMovies = XMCMovie.allObjects() // This query will only grab movies with ratings less than the provided amount let allMovies = XMCMovie.objectsWhere("tomatometer < 60") // Loop through the results and add them to our movies array for index in 0...allMovies.count-1 { let movie = allMovies[index] as XMCMovie movies.append(movie) } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 var movies = [ XMCMovie ] ( ) var index = 0 override func viewDidLoad ( ) { super . viewDidLoad ( ) // Easily see where the realm database is located so we can open it with the Realm Browser println ( RLMRealm . defaultRealm ( ) . path ) // Simulate grabbing data from a web service & creating XMCMovie objects XMCApi . requestOpeningMovies ( ) // This query will grab all of our movies // let allMovies = XMCMovie.allObjects() // This query will only grab movies with ratings less than the provided amount let allMovies = XMCMovie . objectsWhere ( "tomatometer < 60" ) // Loop through the results and add them to our movies array for index in 0 ... allMovies . count - 1 { let movie = allMovies [ index ] as XMCMovie movies . append ( movie ) } }

Alternatively, you can play around by removing the comment from line 14 (and adding a comment to line 17). When you launch the app you should see all 5 movies returned.

Challenge

Here we go! Grab the project and experiment with Realm! Here are a few more ideas for you to play with.

Add a notification block that simply logs to the console when a write happens

Add a button that deletes a movie from the database. Verify that it worked by looking at the Realm browser

Add a new model object, Director, and create the appropriate relationship. You’ll also need to add the appropriate data to the simulated API.

Explore Review the manual installation instructions under getting started (video included) if you get stuck (and don’t hesitate to contact me).

[alert color=”yellow”]github.com project[/alert]

Question & Answer

[alert color=”blue”]I’m completely new to iOS development. Should I start with Realm?[/alert]

Yes. It’s so easy to get up and running with Realm and it works. It’ll give you a great opportunity to learn more about the overall picture. You can always move to Core Data at a later date if you find a need (job prospect, for example). At the same time you should seek out basic information on databases so you’ll better understand how they’re constructed and how they work in your apps.

[alert color=”blue”]I’m seeing strange generator and protocol errors when I try to build. What happened?[/alert]

You likely installed Realm via CocoaPods. It’s important that you manually install Realm and add RLMSupport.swift to your project.

Takeaway

All in all I’m becoming a very big fan of Realm and may begin using it for a new project I started a couple of weeks ago. I’m a massive fan of clean code and reducing complexity. I have little desire to bloat my code with unnecessary lines of code if I can get the same results from an alternative. That’s exactly why I’m enjoying Swift so much. While it’s not as big of a deal to me since I understand multithreaded programming + Core Data, I’d still rather not deal with that complexity if I don’t need to.

There is clearly much more to explore (threading, versions, migrations, testing, debugging), but I hope this served as a nice and warm introduction. Grab the sample project and play around!

Your Turn

Are you using Realm in your project? How is it going?