This is not the current version. View the latest documentation
Getting Started
Installation
Manual Installation (Objective‑C & Swift)
- Download the latest release of Realm and extract the zip.
- Drag
Realm.framework
from theios/
orosx/
directories to the File Navigator of your Xcode project. Make sure Copy items into destination group’s folder is selected and press Finish. - Click on your project in the Xcode File Navigator. Select your app’s target and go to the Build Phases tab. Under Link Binary with Libraries press + and add libc++.dylib.
- If using Realm with Swift, drag the file at
Swift/RLMSupport.swift
into the File Navigator of your Xcode project, checking the “Copy items if needed” checkbox.
Installation via CocoaPods (Objective‑C Only)
If you use CocoaPods…
- Add the following to your Podfile:
pod "Realm"
. - From the command line, run
pod install
. - Use the
.xcworkspace
file generated by CocoaPods to work on your project!
Xcode Plugin
Our Xcode plugin makes it easy to generate new Realm models.
The easiest way to install the Realm Xcode plugin is through Alcatraz under the name “RealmPlugin”. You can also install the plugin manually by opening plugin/RealmPlugin.xcodeproj
contained in the release zip and clicking build. You will need to quit and relaunch Xcode to see our plugin. If you use the Xcode menu to create a new file (File > New > File… — or ⌘N) you should see a new option to create a new Realm model.
Realm Browser
We also provide a standalone app to read and edit .realm databases. You can find it in our release zip under browser/
.
You can generate a test database with dummy data using the menu item Tools > Generate demo database.
If you have installed Xcode 6 beta or are running Yosemite, you may get an “unidentified developer” error when launching the app due to a bug in Apple’s beta releases.
In that case, right click the app and select “Open”. You will still get a warning, but it will have a button to open the app anyway. Subsequent double-clicks will not show a warning at all. (Thanks for the tip @apalancat!)
Examples
You can find example Objective‑C applications in our release zip under ios/examples/
, demonstrating how to use many features of Realm like migrations, use in UITableViewController’s, encryption and much more.
Getting Help
- Sign up for our community newsletter to get regular tips, learn about other use-cases and get alerted of blogposts and tutorials about Realm.
- StackOverflow: look for previous questions under the tag #realm — or open a new one.
- Twitter: contact @realm or use #realm.
- Email: realm-cocoa@googlegroups.com.
Models
Realm data models are defined using traditional Objective‑C interfaces with @properties. Simply subclass RLMObject
to create your Realm data model objects. Realm model objects mostly function like any other Objective‑C objects - you can add your own methods and protocols to them and use them like you would any other object. The only restrictions are that you can only use an object on the thread which they were created, and you can’t access the ivars directly for any persisted properties.
If you have installed our Xcode Plugin there will be a nice template to create the interface and implementation files in the “New File…” dialog.
Relationships and nested data structures are modeled simply by including properties of the target type or RLMArray
’s for typed lists of objects.
@class Person;
// Dog model
@interface Dog : RLMObject
@property NSString *name;
@property Person *owner;
@end
RLM_ARRAY_TYPE(Dog) // define RLMArray<Dog>
// Person model
@interface Person : RLMObject
@property NSString *name;
@property NSDate *birthdate;
@property RLMArray<Dog> *dogs;
@end
RLM_ARRAY_TYPE(Person) // define RLMArray<Person>
// Implementations
@implementation Dog
@end // none needed
@implementation Person
@end // none needed
// Dog model
class Dog: RLMObject {
dynamic var name = ""
dynamic var owner = Person()
}
// Person model
class Person: RLMObject {
dynamic var name = ""
dynamic var birthdate = NSDate(timeIntervalSince1970: 1)
dynamic var dogs = RLMArray(objectClassName: Dog.className())
}
See RLMObject for more details.
Property Types
Realm supports the following property types: BOOL
, bool
, int
, NSInteger
, long
, float
, double
, CGFloat
, NSString
, NSDate
, and NSData
.
Properties of type id
are also supported in model objects, and can store objects of type NSString
, NSDate
and NSData
.
Moreover, subclasses of RLMObject
and RLMArray<x>
are supported to model relationships.
Property Attributes
Note that Realm ignores most Objective‑C property attributes, such as nonatomic
, atomic
, strong
, copy
, weak
, etc., because we use our own optimized storage semantics. The only property that Realm doesn’t ignore is readonly
, which has the effect of preventing a property from being persisted.
To avoid being misleading, we recommend writing models without any property attributes at all (except if you want to use readonly
). If you do set property attributes, they will be used until the object is added to a Realm.
Custom names for getters and setters work normally regardless of whether or not an object is in a Realm.
Customizing Models
Several class methods exist to further specify model information:
+attributesForProperty:
can be overriden to provide property-specific attributes like which properties should be indexed.+defaultPropertyValues
can be overriden to provide default values every time the object is created.+primaryKey
can be overriden to set the model’s primary key to enforce uniqueness for each value.ignoredProperties
can be overriden to prevent Realm from persisting model properties.
Writes
All changes to an object (addition, edition and deletion) have to be done through write transactions.
Realm objects can be instantiated and used standalone just like regular objects. To share objects between threads or re-use them between app launches, you must add and retrieve them from a Realm. You can use transactions like so:
// Create object
Person *author = [[Person alloc] init];
author.name = @"David Foster Wallace";
// Get the default Realm
RLMRealm *realm = [RLMRealm defaultRealm];
// You only need to do this once (per thread)
// Add to Realm with transaction
[realm beginWriteTransaction];
[realm addObject:author];
[realm commitWriteTransaction];
// Create a Person object
let author = Person()
author.name = "David Foster Wallace"
// Get the default Realm
let realm = RLMRealm.defaultRealm()
// Add to the Realm inside a transaction
realm.beginWriteTransaction()
realm.addObject(author)
realm.commitWriteTransaction()
After you have added the object to the Realm you can continue using it, and all changes you make to it from now on will be persisted and available from other threads that uses the same realm.
Please note that writes block each other, and will block the thread they are made on if other writes are in progress. This is similar to any other persistence solution, so we do recommend that you use the usual best-practices for that situation, namely offloading your writes to a separate thread
Please note that thanks to Realm’s MVCC architecture, reads are not blocked while a write transaction is open! This means that unless you need to make simultaneous writes from many threads at once, you should favor larger write transactions that do more work over many fine-grained write transactions.
See RLMRealm and RLMObject for more details.
Queries
All fetches (including queries) are lazy in Realm, and the data is never copied.
A note about using Realm Arrays (RLMArray): All successful calls to retrieval and querying methods return the resulting collection of RLMObjects in an RLMArray. RLMArrays can be manipulated similarly to a standard NSArray; however, RLMArrays are typed, meaning they only hold RLMObjects of the same RLMObject subclass type. For more details see RLMArray.
Retrieving Objects by Type
The most basic method for retrieving objects from a Realm is [RLMObject allObjects]
, which returns all RLMObject instances of the same subclass type from the default Realm.
// On the default Realm:
RLMArray *dogs = [Dog allObjects]; // retrieves all Dogs from the default Realm
// On a specific Realm
RLMRealm *petsRealm = [RLMRealm realmWithPath:@"pets.realm"]; // get a specific Realm
RLMArray *otherDogs = [Dog allObjectsInRealm:petsRealm]; // retrieve all Dogs from that Realm
// Query the default Realm
let dogs = Dog.allObjects()
// Query a specific Realm
let petsRealm = RLMRealm.realmWithPath("pets.realm")
let otherDogs = Dog.allObjectsInRealm(petsRealm)
Querying with Predicates
If you’re familiar with NSPredicate, then you already know how to query in Realm. RLMObjects, RLMRealm and RLMArray all provide methods that allow you to query for specific RLMObjects instances by simply passing in an NSPredicate instance, predicate string, or predicate format string just as you would with an NSObject instance.
For example, the following would extend our earlier example by calling [RLMObject objectsWhere:]
to retrieve all dogs with the color tan and names beginning with ‘B’ from the default Realm:
// Using a predicate string
RLMArray *tanDogs = [Dog objectsWhere:@"color = 'tan' AND name BEGINSWITH 'B'"];
// … Or using an NSPredicate object
NSPredicate *pred = [NSPredicate predicateWithFormat:@"color = %@ AND name BEGINSWITH %@",
@"tan", @"B"];
RLMArray *tanDogs2 = [Dog objectsWithPredicate:pred];
// Query using a predicate string:
let tanDogs = Dog.objectsWhere("color = 'tan' AND name BEGINSWITH 'B'")
// Query using an NSPredicate object:
let predicate = NSPredicate(format: "color = %@ AND name BEGINSWITH %@", "tan", "B")
let tanDogs2 = Dog.objectsWithPredicate(predicate)
See Apple’s Predicates Programming Guide for more information about building predicates. Here are some details about our Predicates implementation:
- The comparison operands can be property names or constants. At least one of the operands must be a property name.
- The comparison operators ==, <=, <, >=, >, !=, and BETWEEN are supported for int, long, float, double, and NSDate property types.
- The comparison operators == and != are supported for bool properties.
- For NSString and NSData properties, we support the ==, !=, BEGINSWITH, CONTAINS, and ENDSWITH operators.
- Realm supports the following compound operators: “AND”, “OR”, and “NOT”.
- Note, although there’s no support for the aggregate expression type, we do support the BETWEEN operator type using object values, e.g.
RLMArray *results = [Person objectsWhere:@"age BETWEEN %@", @[42, 43]];
For more, see [RLMObject objectsWhere:]
.
Ordering Results
In many cases it is desirable to be able to have a retrieval or query operation return an ordered result set. To enable this, RLMArray supports method that allow you to specify a property name to order the result set by.
For example, the following calls [RLMObject objectsWhere:where:]
to sort the returned dogs from the example above alphabetically by name:
// Using a string (sort is ascending by default)
RLMArray *sortedDogs = [[Dog objectsWhere:@"color = 'tan' AND name BEGINSWITH 'B'"]
arraySortedByProperty:@"name" ascending:YES];
// Using a string (sort is ascending by default)
var sortedDogs = Dog.objectsWhere("color = 'tan' AND name BEGINSWITH 'B'").arraySortedByProperty("name", ascending: true)
For more, see [RLMObject objectsWhere:]
and [RLMArray arraySortedByProperty:ascending:]
.
Chaining Queries
Another advantage of Realm’s use of regular objects to store your application data is the ability to chain queries with very little transactional overhead, compared to traditional databases that require a separate trip to the database server for each successive query.
For example, if we wanted a result set for just the tan colored dogs, and the tan colored dogs whose names also started with ‘B’, you might chain two queries like this:
RLMArray *tanDogs = [Dog objectsWhere:@"color = 'tan'"];
RLMArray *tanDogsWithBNames = [tanDogs objectsWhere:@"name BEGINSWITH 'B'"];
let tanDogs = Dog.objectsWhere("color = 'tan'")
let tanDogsWithBNames = tanDogs.objectsWhere("name BEGINSWITH 'B'")
Realms
The Default Realm
You may have noticed so far that we have always initialized access to our realm
variable by calling [RLMRealm defaultRealm]
. That method returns an RLMRealm object that maps to a file called “default.realm” under the Documents folder of your app. Write transactions will be automatically written to disk at that location for you, and likewise for reads.
Other Realms
It’s sometimes useful to have multiple realms, persisted at different locations, for example if you have different data groupings, different databases per feature, or you need to package some read-only files with your app, separate from the database your users will be editing. See [RLMRealm realmWithPath:]
and [RLMRealm realmWithPath:readOnly:error:]
for more info.
Please note that the path passed to [RLMRealm realmWithPath:]
must be in a location with write permissions. Some good locations to store writable realm files are the “Documents” directory on iOS and the “Application Support” directory on OSX.
Using a Realm Across Threads
If you want to access the same realm file from different threads, you must call [RLMRealm defaultRealm]
, [RLMRealm realmWithPath:]
or [RLMRealm realmWithPath:readOnly:error:]
to get a different Realm object for every thread of your app. As long as you specify the same path, all RLMRealm objects will map to the same file on disk. Do not share RLMRealm objects across threads.
In-Memory Default Realm
The Default Realm is persisted to disk by default, but you can also use it purely in memory by calling the following line before any call to [RLMRealm defaultRealm]
.
[RLMRealm useInMemoryDefaultRealm];
RLMRealm *realm = [RLMRealm defaultRealm]; // Only call this line after!
// You must call this method before accessing the default Realm
RLMRealm.useInMemoryDefaultRealm()
let realm = RLMRealm.defaultRealm()
This option is only available on the default Realm.
If you use your default Realm in memory, it will not persist your RLMObjects, meaning data will not be saved across app launches. However, all other features of Realm will work as expected, including querying, relationships and thread-safety. This is a useful option if you need flexible data access without the overhead of disk persistence.
Notice: When the in-memory Realm goes out of scope with no references to it, all data is freed. We are considering improving the API and semantics to make this feature more intuitive.
Relationships
Any two RLMObjects can be linked together. Assuming your Person model has already been defined (see above) let’s create another model called Dog:
// Dog.h
@interface Dog : RLMObject
@property NSString *name;
@end
class Dog: RLMObject {
dynamic var name = ""
}
Many-to-One
Simply declare a property with the type of one of your RLMObject subclasses:
// Dog.h
@interface Dog : RLMObject
... // other property declarations
@property Person *owner;
@end
class Dog: RLMObject {
... // other property declarations
dynamic var owner = Person()
}
This will create a property with name owner and type Person. You can assign and read it just like you would any other property:
Person *jim = [[Person alloc] init];
Dog *rex = [[Dog alloc] init];
rex.owner = jim;
let jim = Person()
let rex = Dog()
rex.owner = jim
When you do queries, children will not be fetched into memory at query time, but you will be able to navigate the links automatically without having to manually fetch children, i.e. doing calls such as rex.owner.address.country
will automatically traverse the object graph for you to return the objects needed.
Many-to-Many
You can establish a relationship to several objects from a single object via an RLMArray<Object> property declaration. RLMArrays are basically containers of RLMObjects, that behave very much like an NSArray, with the notable exception that they are typed.
To add a “dogs” property on our Person model, that links to multiple dogs, we must first define an RLMArray<Dog>
type. This is done via a macro at the bottom of the corresponding model interface:
//Dog.h
@interface Dog : RLMObject
... // property declarations
@end
RLM_ARRAY_TYPE(Dog) // Defines an RLMArray<Dog> type
// Not needed in Swift
You can then declare properties of the RLMArray<Dog>
type:
// Person.h
@interface Person : RLMObject
... // other property declarations
@property RLMArray<Dog> *dogs;
@end
class Person: RLMObject {
... // other property declarations
dynamic var dogs = RLMArray(objectClassName: Dog.className())
}
You can access and assign RLMArray properties as usual:
// Jim is owner of Rex and all dogs named "Fido"
RLMArray *someDogs = [Dog objectsWhere:@"name contains 'Fido'"];
[jim.dogs addObjectsFromArray:someDogs];
[jim.dogs addObject:rex];
let someDogs = Dog.objectsWhere("name contains 'Fido'")
jim.dogs.addObjectsFromArray(someDogs)
jim.dogs.addObject(rex)
Note: RLMArray properties on models are “copy on write”. Any direct assignment of that property will copy the references to the objects from the assignee into the assigned property. In the example above, that means that any dogs added to some_dogs
after jim.dogs = some_dogs;
would not also be added to jim.dogs
.
Notifications
The auto-updating Realm will send out notifications every time the underlying Realm is updated. These notifications can be observed by registering a block:
// Observe Realm Notifications
self.token = [realm addNotificationBlock:^(NSString *note, RLMRealm * realm) {
[myViewController updateUI];
}];
let token = realm.addNotificationBlock { note, realm in
viewController.updateUI()
}
You can use the token to unsubscribe from the notification later on. We recommend you store this token on your Main class, as losing reference to it will automatically unsubscribe you from the notification.
See [Realm addNotificationBlock:]
and [Realm removeNotificationBlock:]
for details.
Background Operations
Realm can be very efficient when writing large amounts of data by batching together multiple writes within a single transaction. Transactions can also be performed in the background using Grand Central Dispatch to avoid blocking the main thread. RLMRealm objects are not thread safe and cannot be shared across threads, so you must get an RLMRealm instance in each thread/dispatch_queue in which you want to read or write. Here’s an example of inserting a million objects in a background queue:
dispatch_async(queue, ^{
// Get realm and table instances for this thread
RLMRealm *realm = [RLMRealm defaultRealm];
// Break up the writing blocks into smaller portions
// by starting a new transaction
for (NSInteger idx1 = 0; idx1 < 1000; idx1++) {
[realm beginWriteTransaction];
// Add row via dictionary. Order is ignored.
for (NSInteger idx2 = 0; idx2 < 1000; idx2++) {
[Person createInRealm:realm
withObject:@{@"name" : [self randomString],
@"birthdate" : [self randomDate]}];
}
// Commit the write transaction
// to make this data available to other threads
[realm commitWriteTransaction];
}
});
dispatch_async(queue) {
// Get realm and table instances for this thread
let realm = RLMRealm.defaultRealm()
// Break up the writing blocks into smaller portions
// by starting a new transaction
for idx1 in 0..<1000 {
realm.beginWriteTransaction()
// Add row via dictionary. Order is ignored.
for idx2 in 0..<1000 {
Person.createInDefaultRealmWithObject(
["name": "\(idx1)", "birthdate": NSDate(timeIntervalSince1970: idx2)])
}
// Commit the write transaction
// to make this data available to other threads
realm.commitWriteTransaction()
}
}
See RLMRealm for more details.
REST APIs
Due to Realm’s small memory footprint and fast object querying, you can retrieve and persist as much as ten times the data you might normally fetch from a REST API, then query it directly from a Realm. This has several advantages:
- Retrieve large amounts of data with a single API call or by pre-fetching in the background, then persist it in the Realm.
- Since Realm is thread-safe, you can easily asynchronize this task and update your views once the REST call has completed.
- Query data directly from the Realm, rather than waiting for the API to process complex queries server side.
- Offer a strong user experience while offline since you can cache large datasets that you can still query and update.
- Reduce server-side load: while the first few interactions may generate more traffic than usual, having a cache dataset can help reduce server-side load over time by reducing the frequency at which have to fetch the same data over and over.
Best Practices
- Asynchronous Requests — Since Realm can hold a large amount of data in a small footprint, a good practice is to execute multiple API requests in the background to build a larger local data set. This creates a more seamless user experience in your app, as the user never experiences wait time for an API request to return on the main thread. You can use Notifications to monitor the progress of your REST requests.
- Caching datasets larger than what will be immediately displayed to the user — We recommend you pre-fetch data as often as possible and store it locally in your Realms. For example, if you are only displaying 10 results per page in a List View, fetch the next 3-4 pages of results if the user is likely to visit them. You should consider doing the same with map view (fetch results for the surrounding areas) or for your app in general (pre-fetch data for screens the user is likely to browse to during the normal course of his usage).
- Insert-or-update — If your dataset has a unique identifier such as a primary key (or set of unicity conditions), you can use it to easily code insert-or-update logic: when receiving a response from the API, you can check if each record already exists by querying the Realm for it. If it does exist locally, update with the latest details from the response, if not, insert it into the Realm.
Example
The following is a simple example of how you can use Realm with a REST API. In this example, we’ll retrieve a JSON-formatted data set from the foursquare API, then save it as Realm Objects in the default Realm.
For a realtime example of a similar use case in action, check out our video demo.
First we create an instance of the default Realm to persist the data to, and fetch our data set from the API. For simplicity in this example we use [NSData initWithContentsOfURL]
.
RLMRealm *realm = [RLMRealm defaultRealm];
// Call the API
NSData *response = [ [NSData alloc] initWithContentsOfURL:
[NSURL URLWithString:@"https://api.foursquare.com/v2/venues/search?near=San%20Francisco&limit=50"]];
// Deserialize the response to JSON
NSDictionary *json = [[ NSJSONSerialization
JSONObjectWithData:response
options:kNilOptions
error:&error ] objectForKey:@"response"];
// Call the API
let url = NSURL(string: "https://api.foursquare.com/v2/venues/search?near=San%20Francisco&limit=50")
let response = NSData(contentsOfURL: url)
// De-serialize the response to JSON
let json = NSJSONSerialization.JSONObjectWithData(response,
options: NSJSONReadingOptions(0),
error: nil)["response"]
The response contains a JSON array of venues similar to this:
{
"venues": [
{
"id": "4c82f252d92ea09323185072",
"name": "Golden Gate Park",
"contact": {
"phone": "4152522590"
},
"location": {
"lat": 37.773835608329,
"lng": -122.41962432861,
"postalCode": "94103",
"cc": "US",
"state": "California",
"country": "United States"
}
}
]
}
There are several ways we may want to import this JSON into our Realm. You could read the NSDictionary and map the properties to a single RLMObject manually via a custom insert function. For the sake of this example, we will instead directly insert the NSDictionary in the Realm and have it automatically be mapped to a hierarchy of RLMObjects that will be created on the fly for us. For this to work, we need an RLMObject structure whose properties will match all the keys in the JSON exactly. JSON keys not matched by an RLMObject property will be ignored on insert. The following RLMObject declarations would work:
// Venue.h
@interface Venue : RLMObject
@property NSString *id;
@property NSString *name;
@property Contact *contact;
@property Location *location;
@end
RLM_ARRAY_TYPE(Venue)
// Contact.h
@interface Contact : RLMObject
@property NSString *phone;
@end
RLM_ARRAY_TYPE(Contact)
// Location.h
@interface Location : RLMObject
@property double lat; // latitude
@property double lng; // longitude
@property NSString *postalCode;
@property NSString *cc;
@property NSString *state;
@property NSString *country;
@end
RLM_ARRAY_TYPE(Location)
class Contact: RLMObject {
dynamic var phone = ""
}
class Location: RLMObject {
dynamic var lat = 0.0 // latitude
dynamic var lng = 0.0 // longitude
dynamic var postalCode = ""
dynamic var cc = ""
dynamic var state = ""
dynamic var country = ""
}
class Venue: RLMObject {
dynamic var id = ""
dynamic var name = ""
dynamic var contact = Contact()
dynamic var location = Location()
}
Since the result set is given to us as an array we have to create an object for each element by calling [Venue createInDefaultRealmWithObject:]
. This creates Venue
and its child objects from a JSON representation and adds the newly created obejcts to the default Realm:
//Extract the array of venues from the response
NSArray *venues = json[@"venues"];
[realm beginWriteTransaction];
// Save one Venue object (and dependents) for each element of the array
for (NSDictionary *venue in venues) {
[Venue createInDefaultRealmWithObject:venue];
}
[realm commitWriteTransaction];
//Extract the array of venues from the response
let venues = json["venues"] as [NSDictionary]
realm.beginWriteTransaction()
// Save one Venue object (and dependents) for each element of the array
for venue in venues {
Venue.createInDefaultRealmWithObject(venue)
}
realm.commitWriteTransaction()
Migrations
When working with any database, it is likely your data model will change over time. Since data models in Realm are defined as standard Objects, changing them is as easy as changing the interface of the corresponding RLMObject subclass. For example, suppose we have the following interface in ‘Person.h’:
@interface Person : RLMObject
@property NSString *firstName;
@property NSString *lastName;
@property int age;
@end
class Person: RLMObject {
dynamic var firstName = ""
dynamic var lastName = ""
dynamic var age = 0
}
Next, we want to update the data model to require a ‘fullName’ property, rather than separate first and last names. To do this, we simply change the subclass interface to the following:
@interface Person : RLMObject
@property NSString *fullName;
@property int age;
@end
class Person: RLMObject {
dynamic var fullName = ""
dynamic var age = 0
}
Just changing your code to the new definition will work fine, if you have no data stored on disk under the old schema. But if you do, there will be a mismatch between what Realm sees defined in code, and the data Realm sees on disk. In short if you change your schema definition in one of your models and then instantiate a realm with [RLMRealm defaultRealm]
(or a similar realm instantiation call), that call will throw an NSException with a message that you should run a migration.
Realms that contain at least one class that has been redefined must be migrated to the current schema before they can be accessed. To make this process easy, Realm provides specialized classes and methods for handling schema migration.
Migrating a Realm to a new schema takes just two steps, and must be done before anything else so we recommend you complete them from inside your [AppDelegate didFinishLaunchingWithOptions:]
:
Performing a Migration
You define a migration by implementing an RLMMigrationBlock
which you pass into a call to [RLMRealm migrateDefaultRealmWithBlock:]
for the default Realm or [RLMRealm migrateRealmAtPath:withBlock:]
for other Realm instances. Your migration block provides all the logic for converting data models from previous schemas to the new schema.
For example, suppose we want to migrate the ‘Person’ subclass from above. To do this, the minimal necessary migration block would look like the following:
// Inside your [AppDelegate didFinishLaunchingWithOptions:]
RLMMigrationBlock migrationBlock = ^NSUInteger(RLMMigration *migration,
NSUInteger oldSchemaVersion) {
// We haven’t migrated anything yet, so oldSchemaVersion == 0
if (oldSchemaVersion < 1) {
// Nothing to do!
// Realm will automatically detect new properties and removed properties
// And will update the schema on disk automatically
}
// Return the latest version number (always set manually)
// Must be a higher than the previous version or an RLMException is thrown
return 1;
};
// Apply the migration block above to the default Realm
[RLMRealm migrateDefaultRealmWithBlock:migrationBlock];
// Inside your application(application:didFinishLaunchingWithOptions:)
let migrationBlock: RLMMigrationBlock = { migration, oldSchemaVersion in
if oldSchemaVersion < 1 {
// Nothing to do!
// Realm will automatically detect new properties and removed properties
// And will update the schema on disk automatically
}
// Return the latest version number (always set manually)
// Must be a higher than the previous version or an RLMException is thrown
return 1
}
// Apply the migration block above to the default Realm
RLMRealm.migrateDefaultRealmWithBlock(migrationBlock)
At the very minimum, all we need to do is return 1;
to indicate the that the schema has been upgraded (automatically) by Realm.
N.B. The version number returned can be either an integer (version) or timestamp (epoch). We recommend you set it manually in the code as it defines the current version of the schema your app is using.
While you must manually return the version number to the app at the end of your migration block, note that Realm takes care of updating the schema version number inside the realm on disk.
While this is the minimal acceptable migration, we probably want to use this block to pre-fill the “fullName” property with something more meaningful. Within the migration block we can call [RLMMigration enumerateObjects:block:]
to enumerate each Realm Object of a certain type, and apply any necessary migration logic. Notice how for each enumeration the existing RLMObject instance is accessed via an oldObject
variable, and the updated instance is accessed via newObject
:
// Inside your [AppDelegate didFinishLaunchingWithOptions:]
// Perform a migration defining the migration block inline
[RLMRealm migrateDefaultRealmWithBlock:^NSUInteger(RLMMigration *migration, NSUInteger oldSchemaVersion) {
// We haven’t migrated anything yet, so oldSchemaVersion == 0
if (oldSchemaVersion < 1) {
// The enumerateObjects:block: method iterates
// over every 'Person' object stored in the Realm file
[migration enumerateObjects:Person.className
block:^(RLMObject *oldObject, RLMObject *newObject) {
// combine name fields into a single field
newObject[@"fullName"] = [NSString stringWithFormat:@"%@ %@",
oldObject[@"firstName"],
oldObject[@"lastName"]];
}];
}
// Return the latest version number (always set manually)
// Must be a higher than the previous version or an RLMException is thrown
return 1;
}];
// Inside your application(application:didFinishLaunchingWithOptions:)
// Perform a migration defining the migration block inline
RLMRealm.migrateDefaultRealmWithBlock { migration, oldSchemaVersion in
if oldSchemaVersion < 1 {
// The enumerateObjects:block: method iterates
// over every 'Person' object stored in the Realm file
migration.enumerateObjects(Person.className()) { oldObject, newObject in
// combine name fields into a single field
let firstName = oldObject["firstName"] as String
let lastName = oldObject["lastName"] as String
newObject["fullName"] = "\(firstName) \(lastName)"
}
}
// Return the latest version number (always set manually)
// Must be a higher than the previous version or an RLMException is thrown
return 1
}
Once the migration is successfully completed, the Realm and all of its Objects can be accessed as usual by your app.
Adding more versions
Suppose now we change the data model for the ‘Person’ subclass yet again, for a total of three different schemas:
// v0
@interface Person : RLMObject
@property NSString *firstName;
@property NSString *lastName;
@property int age;
@end
// v1
@interface Person : RLMObject
@property NSString *fullName; // new property
@property int age;
@end
// v2
@interface Person : RLMObject
@property NSString *fullName;
@property NSString *email; // new property
@property int age;
@end
// v0
class Person: RLMObject {
dynamic var firstName = ""
dynamic var firstName = ""
dynamic var age = 0
}
// v1
class Person: RLMObject {
dynamic var fullName = "" // new property
dynamic var age = 0
}
// v2
class Person: RLMObject {
dynamic var fullName = ""
dynamic var email = "" // new property
dynamic var age = 0
}
The logic in our migration block might look like the following.
[RLMRealm migrateDefaultRealmWithBlock:^NSUInteger(RLMMigration *migration, NSUInteger oldSchemaVersion) {
// The enumerateObjects:block: method iterates
// over every 'Person' object stored in the Realm file
[migration enumerateObjects:Person.className block:^(RLMObject *oldObject, RLMObject *newObject) {
// Add the 'fullName' property only to Realms with a schema version of 0
if (oldSchemaVersion < 1) {
newObject[@"fullName"] = [NSString stringWithFormat:@"%@ %@",
oldObject[@"firstName"],
oldObject[@"lastName"]];
}
// Add the 'email' property to Realms with a schema version of 0 or 1
if (oldSchemaVersion < 2) {
newObject[@"email"] = @"";
}
}];
// Return the latest version number (always set manually)
// Must be a higher than the previous version or an RLMException is thrown
return 2;
}];
RLMRealm.migrateDefaultRealmWithBlock { migration, oldSchemaVersion in
// The enumerateObjects:block: method iterates
// over every 'Person' object stored in the Realm file
migration.enumerateObjects(Person.className()) { oldObject, newObject in
// Add the 'fullName' property only to Realms with a schema version of 0
if oldSchemaVersion < 1 {
let firstName = oldObject["firstName"] as String
let lastName = oldObject["lastName"] as String
newObject["fullName"] = "\(firstName) \(lastName)"
}
// Add the 'email' property to Realms with a schema version of 0 or 1
if oldSchemaVersion < 2 {
newObject[@"email"] = ""
}
}
// Return the latest version number (always set manually)
// Must be a higher than the previous version or an RLMException is thrown
return 2
}
For a more complete look at the implementation of a data schema migration, check out our migration sample app.
Linear Migrations
Suppose we have two users for our app: JP and Tim. JP updates the app very often, but Tim happens to skip a few versions between sessions in. It’s likely that JP has seen every new version of our app, and every schema upgrade in sequence: he downloaded a version of the app that took him from v0 to v1, another update that took him from v1 to v2. In contrast, it’s possible that Tim will download an update of the app that would need to take him from v0 to v2 immediately. Structuring your migration blocks with non-nested if (oldSchemaVersion < X)
calls ensures that they will see all necessary upgrades, no matter which schema version they start from.
Another scenario may arise in the case of users who skipped versions of your app. If you delete a property “say address” at version 2 and re-introduce it at version 3, and a user jumps from version 1 to version 3, Realm will not be able to automatically detect the deletion of the “address” property, as there will be no mismatch between the schema on disk and the schema in the code for that property. This will lead to Tim’s Person object having a v3 address property that has the contents of the v1 address property. This may not be a problem, unless you changed the internal storage representation of that property between v1 and v3 (say, went from an ISO address representation to a custom one). To avoid this, we recommend you nil out or the address property on the if (oldSchemaVersion < 3)
statement, guaranteeing that all realms upgraded to version 3 will have a correct dataset.
Next Steps
You can look at our examples to see Realm used in practice in an app. (We’re getting more samples ready!)
Happy hacking! You can always talk to a live human developer on realm-cocoa.
Current Limitations
Realm is currently in beta and we are continuously adding features and fixing issues while working towards a 1.0 release. Until then, we’ve compiled a list of our most commonly hit limitations.
Please refer to our GitHub issues for a more comprehensive list of known issues.
Realm in CocoaPods does not work for Swift projects
CocoaPods doesn’t support Swift projects yet (see their GitHub issue #2222). To use Realm in a Swift project, please follow the instructions above.
Fine-grained notifications are not yet supported
While it is possible to receive notifications when a realm changes (see Notifications), it is not currently possible to determine what was added/removed/moved/updated from that notification. We will be adding this feature in the near future.
NSDate is truncated to the second
Persisting an NSDate
with a fractional number of seconds will truncate the date to the second. A fix for this is in progress. See GitHub issue #875 for more details. In the mean time, you can store NSTimeInterval
properties with no loss in precision.
Subclassing is not supported
Realm objects must be created by subclassing RLMObject
directly. Therefore, a Realm object cannot inherit from another Realm object. See GitHub issue #652 for more details and work-arounds.
Realm Object Setters & Getters cannot be overriden
Since Realm overrides setters and getters to back properties directly by the underlying database, you cannot override them on your objects. A simple workaround is to create new, realm-ignored properties, whose accessors can be overriden, and can call other setters/getters.
KVO is not supported
Realm doesn’t support KVO but has its own notification mechanism (see Notifications).
Realm files cannot be accessed by concurrent processes
Although Realm files can be accessed by multiple threads concurrently, they can only be accessed by a single process at a time. This has implications for iOS 8 extensions and OSX applications. Different processes should either copy Realm files or create their own. Multi-process support is forthcoming.
FAQ
How big is the Realm library?
Once your app is built for release, Realm should only add around 1MB to its size. The releases we distribute are significantly larger (around 30MB for each .Framework) because they include support for more architectures (ARM, ARM64, x86 for the simulator) and some debug symbols, which will all be stripped by Xcode automatically when you build your app.
Can I use Realm for OS X?
Yes! At the moment you just need to build from source, which will create a separate, OS X compatible .Framework you can use in your apps. We are working on adding it to our CocoaPod as well.
Should I use Realm in production applications?
Realm has been used in production in commercial products since 2012.
You should expect our Objective‑C APIs to break as we evolve the product from community feedback — and you should expect more features & bugfixes to come along as well.
The Swift API should only be used in production after Swift has been stabilized by Apple itself; right now our API is experimental due to the fact Apple is continuously changing the language with every new beta release.
Do I have to pay to use Realm?
No, Realm for iOS is entirely free to use, even in commercial projects.
How do you all plan on making money?
We’re actually already generating revenue selling enterprise products and services around our technology. If you need more than what is currently in our releases or in realm-cocoa, we’re always happy to chat by email. Otherwise, we are committed to developing realm-cocoa in the open, and to keep it free and open-source under the Apache 2.0 license.
I see references to “tightdb” or a “core” in the code, what is that?
TightDB is the old name of our core C++ storage engine. The core is not currently open-source but we do plan on open-sourcing it, also under the Apache 2.0 license, once we’ve had a chance to clean it, rename it and finalize our synchronization implementation inside of it. In the meantime, its binary releases are made available under the Realm Core (TightDB) Binary License.