Tim Oliver, creator of the iComics app, gave a talk featuring Realm in his project at the Perth iOS Meetup. His talk covered gave a quick background of his transition to and experiences using Realm in place of Core Data, a switch he made in just one evening. Read on for more details on how he did it.
I’ve been working on an app called iComics, a comic book reader, that had previously been on Core Data. When I was working on iComics, I’d have problems when a background thread would import a comic, tell the main thread the comic imported, then send an object with entirely empty fields. When I tested installing a new version of the app over an older version, I found that Core Data’s auto-migration was corrupting half the database and making the app very unstable. A manual Core Data migration would have required a ridiculous amount of code, and I decided to switch to Realm.
Introduction to Realm (3:43)
I was introduced to Realm by a friend, but originally thought that it would be too much effort to switch databases. Realm is a database system similar to Core Data but a lot more simple and designed expressly for smartphones. It’s a fully integrated custom C++ integration with a low level core called tightDB that feeds into a very light Objective-C and Swift wrapper. I also thought I’d have to take a performance hit, but found the case to be otherwise.
Coding Demo (7:19)
It’s fairly easy to use Realm. After downloading the zipped file from the Realm website, you just copy in a normal iOS static framework. Since Realm is built on a C++ core, you also have to link the “libc++.dylib”. A model object represents an entity in the database; in iComics, I have a table for comic book collections and pages.
The way Realm works is that on the hard drive you have .realm files, which are basically the same as .sqlite files. It takes only a single class method to access an object, whereas Core Data would need many more lines of code. The default Realm acts like a singleton but is not actually one; it doesn’t have to store any of its own state information.
After creating objects, they are actually Realm-less. To add them to a Realm, we have to actually start a write transaction, since Realms are by default read-only. One side neat thing about Realm is that it periodically checks to see if theres a new version available on the website and tells you in the console. The download from the Realm site also includes a quick little app that lets you open Realm files. You can also chain queries and get one set of results.
Another cool thing about Realm is that it supports many data types, some that Core Data doesn’t even support. You can’t use NSInteger or CGFloat in Core Data, but you can in Realm. You can create relations and even an array of Realm objects.
The RLMRealm represents a reference to a data store file, with the defaultRealm in the Documents library. You can create Realms fully in memory, adding objects and performing queries, but if the app closes that Realm is gone forever. My guess is this exists if you want to do really thorough quick indexing, where it might be quicker than actually saving it to disk. Repeated calls are cached, and you can do concurrent reads across threads. However, if you open a write transaction, you cannot do concurrent write transactions. There is no chance for data corruption with threads overwriting themselves.
The RLMObject supports petty much every major data type: strings, ints, floats, NSData, NSDates, and not NSArray but a special Realm array that can only hold one type of data. Properties can be converted into database columns, but accessors cannot be overridden. KVO is being worked on for those. Properties can be explicitly ignored. Standard int and float properties are allowed as well. A single property can be made into a primary key and Realm will enforce that. This property must be unique, which can be really useful if you want to pull out a specific object from the Realm by just referring to its primary key. Fetch queries are class methods on these objects. However, you cannot modify an RLMObject outside of a write transaction, and they cannot be passed between threads, although this can be worked around.
RLMResults behave exactly like NSArray, but of a single type. As I mentioned earlier, they can also be chained to refine or sort results.
Concurrency Demo (26:44)
To create a primary key, all you need is the following code. If you try to set two objects to primaryKey, you’ll get an exception.
@ implementation Captain
+ (NSString *)primaryKey
Once an object is added to a Realm, you can actually see what Realm it’s attached to. As a result, any object you create is implicitly tied to a Realm that is tied to a thread. To work around the fact that you can’t pass objects between threads, you get the ID in another instance of the same Realm that’s created and controlled in a separate thread. I didn’t notice any performance hits in iComics when doing import operations, so it was all very quick and simple.
When you create an object on background threads and hit save, the Realm on the main thread will be informed that it has been updated and all the base will be updated. If you create an object and try to access it from a Realm on a different thread, that update will not persist. You have to call a property called “refresh” on the object.
Rules of Concurrency
- One RLMRealm object for each thread.
- Objects must be re-fetched on new threads.
- Using [RLMObject objectForPrimaryKey:] works great!
- All other realms are locked when a write transaction is in progress.
- Objects on the main thread will be automatically refreshed.
- [RLMRealm addNotificationBlock:] can be used to detect updates, but not specific object changes.
Realm + iComics (43:57)
My iComics database has a list of entities for collections and for comics. I have also store NSDates, integers for the number of pages, and references to the collections they’re in. An example of objects having relationships with each other is that everything in my new iComics is in a group. Groups are just folders on disk with a UUID, so comics can be physically in the folder and associated with that folder in the database.
I like Realm because it’s strict system will tell you immediately if you’ve done something wrong, compared to Core Data where you could keep going for ages and not realise you’ve broken something. However, when I switched to Realm, I didn’t do migration of peoples’ state data. Realm was smart enough to rebuild the database from scratch but state information like the page that people had read up to.
Some comments from Realm
Everyone at Realm thoroughly enjoyed Tim’s talk and were impressed with his grasp of the technology, despite only being a few months old! There are a few points we’d like to add:
- At one point, an audience member asks how Realm works, then proceed to say “it’s mostly an in-memory database, with persistence thrown in”. This is almost correct, but not quite. Realm is actually a memory-mapped database, which means that even it feels as if you’re modifying data directly in memory while avoiding much of the overhead.
- Tim’s code has a long list of ignored properties in his data models. Since Realm 0.86,
readonlyproperties are automatically ignored, making this code much cleaner.
- Despite what Tim states in the video, Realm does support read-only files.
- An audience member asks if Realm is working on sync. Realm was designed with sync in mind very early on and we consider this to be a very high priority feature. Stay tuned!
- At around the 45 minute mark, Tim says that he had to swizzle
+defaultRealmto override where it was stored. As of Realm 0.88, it’s now possible to specify the location of the default realm. No swizzling necessary :).
Thank you to Tim for the great video. We look forward to seeing more of your work!
Get more development news like this
About the content
This content has been published here with the express permission of the author.