Tim Oliver, from the Cocoa team at Realm, presents a Swift introduction to Realm! He draws comparisons between Realm and other solutions (iOS and Mac), provides an introduction to Realm, and runs through some quick code examples in Swift.
Hi! My name is Tim Oliver, and I joined Realm’s Cocoa team this year; my specialty is Realm Browser. Today I am here to present a Swift introduction to Realm. I will draw some comparisons between Realm and other solutions (iOS and Mac), provide an introduction to Realm, and run through some quick code examples.
I am a rather unique person at Realm because I joined the company having already shipped an app using Realm: iComics, a DRM-free comic book reader. You stick in a .zip file full of JPEGs, and the app will turn it into a book mechanism so you can read any comics you download online. As you could imagine, there is a data component to that. By then, I was using Core Data.
System-Level Solutions: Serialization, SQLite, Core Data (2:01)
Core Data takes a very long time to learn, and you need to be aware of boilerplate code. To set up a Core Data instance you would have to set up a managed-object model, which hooks up to a persistent store coordinator, then hooks up to a context before you can even start reading and writing data. Also, as it is an Apple product, if it breaks, you usually do not know why. You might get a cryptic message, or it might just keep trying to go along in a semi-broken state. In my app, if I was doing an operation on a background thread, the changes would not propagate to the main thread. I would have a semi-functioning fetched results controller showing empty cells. The thread model of Core Data can tricky.
This time last year I moved up from Core Data to SQLite, which means:
- Going from Objective-C and Swift down to C (not great unless you use a third-party library): structured query language.
- Setting up a database on my own with specific primary/foreign keys, knowing what is getting indexed and how data is being saved.
- Writing boilerplate code, making a query and mapping it to your abstraction model layout (Core Data does it automatically, but you have to do it manually in SQLite). If you have a change of schema, you will have to run a series of queries to update user’s tables (and track what states the user’s SQLite file on disk before you start reading from it again).
- Using many third-party libraries.
If set up properly (e.g. indexes set right), you get a really good speed performance. I do open source little controls, and sometimes it does not make sense to have a full-blown data solution.
I like using JSON to serialize state to disk and back again. It is probably not the best for many things, but it is great when you want to have an open source component. Compared to SQLite and Core Data, it is either all (on disk/on memory) or nothing. As a result, if it is a large data set, it will be slow, and there is no threading safety. If you try and write concurrently to two files or read from different threads, you might end up with data conflicting and corrupting your file.
Introducing Realm (5:35)
Realm is an open source database framework. It has been around for just over a year, and there are already many big companies adopting it. They provide great feedback and Realm constantly improves in performance and usability. Realm is a complete replacement for Core Data/SQLite: it is not like another library (e.g. abstraction on top of SQLite). Based on ORM models, Realm works such that you do not have to write any adaptive code from a query to a model as it’s all done for you! It is also thread-safe, ridiculously fast (generally, much faster than SQLite), has simple API, and works cross-platform. Realm serves as a single database solution that will work on both iOS and Android. Best of all, it’s completely free.
Realm is structured such that each type of library syncs with the core written in C++ that contains the blazing fast wizardry. We then interoperate with the various platforms through a series of bindings, including Objective-C, Swift, Java and thus Android, and more on the way!
Installation (9:15)
- Download the .zip from Realm.io.
- Add framework to project. To do so statically, you can just drag the zip straight into your project. However, Realm is also available via CocoaPods and Carthage. Finally, add libc++ since that’s at the Core of Realm.
The Basics — Realm Objects (9:40)
To set up, create an object called Realm let realm = Realm()
. This represents the actual file on-disk — the context for which you will be saving information and then pulling it back later on.
In Swift it is very easy: it is usually a Realm object, which is already preconfigured to the most common uses of it for you. You can change the configuration if you’d like as well with only a couple lines of code.
Realm files can be opened with an app called Realm Browser for Mac, as well as other platforms on their way.
Model Objects (10:30)
To represent that data to get saved to disk is through model objects. Model objects are very easy to set up: create a subclass of ‘Object’, and add your properties; it’s that simple, and all you have to do. Unlike Core Data, you do not have to set up a managed object model, or set up a file with persistent file coordinator, link them together and hang on to them. Realm is smart enough to set up a schema based off this model inside the Realm file at runtime. It solves all of the complications Core Data has you manage manually.
Adding New Objects (11:10)
It supports most of the useful Swift types, and it’s also possible to add new Realm objects as children to Realm objects (even lists of objects straight into Realm files). You could set up really good relationships with very minimal amounts of code:
// Create new Dog object
let newDog = Dog()
newDog.name = "Earl Yippington III"
// Write to Realm file
let realm = Realm()
realm.write {
realm.add(newDog)
}
This demonstrates saving data to disk, which is incredibly simple. We create a new model object around the one you just subclassed, populate it, and then add data to Realm via a write transaction. All you have to do is use realm.write()
, realm.add(Obj)
and you’re done. That’s all you need — a trivial amount of code that’s easy and fast to set up.
Reading Objects From a Realm (12:07)
// Get all Dog objects saved to disk
let dogs = Realm().objects(Dog)
To get the data write back we have to call the Realm object you used to save information to the disk and get the objects back that I want (one-line code). This will get back all the objects, but the good thing is that it is not copying information memory. It is all zero-copy, so even though you have just tried to pull in all objects, they are actually lazy loaded and only done when you try and access properties from them. In doing this, you are not actually creating any overhead or wasting any memory. That also means you eliminate any need for pagination.
Beyond that, you can go on to filter those results. You could set up basic strings that define specific properties, or it even conforms to the majority of NSPredicates or sort by another property. For example, this is querying for puppies and by their names:
// Filter the dogs by age
let puppies = dogs.filter("age < 2")
// Sort the puppies by name
let sortedPuppies = puppies.sort("name")
Updating Objects in a Realm (13:20)
// Get the first dog in the database
let dog = Realm().objects(Dog).first
// INCORRECT - An exception will be thrown
dog.name = "Jabba the Mutt"
// Correct - Object will be updated
Realm().write {
dog.name = "Jabba the Mutt"
}
Realm files need to be open for a write transaction before we can change any of the properties. If you try and modify objects outside of a write transaction, an exception will be triggered straight away. It’s just a matter of opening a write transaction and then updating the property there.
This is done for thread-safety reasons. You can read properties from a Realm object via different threads, but only write from one thread to ensure proper consistency and no data loss.
In short, to achieve thread-safety, you just have to make sure your Realm model objects are updated inside a write transaction as shown above.
Passing Objects Across Threads (14:34)
Realm objects route thread-confined (they have a very strict set of rules - e.g. you cannot pass objects between threads; if you do, you will get an exception straight away). Realm is fast and it is really easy to just re-fetch objects. For these reasons, it is easy to pass objects between threads by setting up a primary key for the object, passing it across threads, and then simply re-fetching the object from the new thread. Although we are working on streamlining this, it’s very simple to get to work.
Here’s an example for passing a primary key:
// Define a dog object with a primary key
class Dog: Object {
dynamic var uuid = NSUUID().UUIDString
override static func primaryKey() {
return "uuid"
}
}
It does not have to be an integer (thankfully), it just has to be unique. A lot of the time it is safe enough just to use a UUID. You just override a function that is part of the Realm objects subclass dictating that is a primary key, which Realm will then index.
Then we will grab a copy of that key, pass the key across the thread and create the same Realm object:
// Pass a Dog object between threads
let dogUUID = myDog.uuid
dispatch_async(dispatch_get_global_queue(0,0)) {
let myNewDog = Realm().objectForPrimaryKey(Dog, dogUUID)
//...
}
When you query for a Realm object on a different thread, you will get that thread’s version of the Realm object, and then pull the object back into your context. The entire process is really easy overall.
Schema Migrations (16:20)
Finally, if you need to change your model, add a new property or pull a property out, you will need to do a schema migration. All Realm files have a schema version. However, if you pull a model object out of a Realm when it has an incompatible schema, you will throw an exception that’s easy to spot when testing.
Instead, you specify a migration, which is telling the Realm object that you want to bump the schema version. You can even supply a block, which will give you a chance to take any pre-existing data out of the Realm file, modify it and then put it straight back into the Realm file with the new schema version. The sample code for this is available here in the docs.
Upcoming Features (17:26)
That’s just the tip of the iceberg. Since Realm is still new, there are many more things coming, including:
- Support for NULL properties
- Fine-grained notifications
- Swift 2.0 support
Check us out here at Realm.io. You can also reach us on Twitter, find the projects up on GitHub, and get any support you’ll need by using #realm in StackOverflow.
Receive news and updates from Realm straight to your inbox