This is not the current version. View the latest documentation

Getting started

If you’re looking to use Realm purely from Swift, consider using Realm Swift instead. The Realm Objective‑C and Realm Swift APIs are not interoperable and using them together is not supported.

Realm Objective‑C enables you to efficiently write your app’s model layer in a safe, persisted and fast way. Here’s what it looks like:

// Define your models like regular Objective‑C classes
@interface Dog : RLMObject
@property NSString *name;
@property NSData   *picture;
@property NSInteger age;
@end
@implementation Dog
@end
RLM_ARRAY_TYPE(Dog)
@interface Person : RLMObject
@property NSString             *name;
@property RLMArray<Dog *><Dog> *dogs;
@end
@implementation Person
@end

// Use them like regular Objective‑C objects
Dog *mydog = [[Dog alloc] init];
mydog.name = @"Rex";
mydog.age = 1;
mydog.picture = nil; // properties are nullable
NSLog(@"Name of dog: %@", mydog.name);

// Query Realm for all dogs less than 2 years old
RLMResults<Dog *> *puppies = [Dog objectsWhere:@"age < 2"];
puppies.count; // => 0 because no dogs have been added to the Realm yet

// Persist your data easily
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
    [realm addObject:mydog];
}];

// Queries are updated in realtime
puppies.count; // => 1

// Query and update the result in another thread
dispatch_async(dispatch_queue_create("background", 0), ^{
    @autoreleasepool {
        Dog *theDog = [[Dog objectsWhere:@"age == 1"] firstObject];
        RLMRealm *realm = [RLMRealm defaultRealm];
        [realm beginWriteTransaction];
        theDog.age = 3;
        [realm commitWriteTransaction];
    }
});

Prerequisites

  • XCode 8.0 or higher
  • Target of iOS 8 or higher, macOS 10.9 or higher, or any version of tvOS or watchOS

Installation

  1. Download the latest release of Realm and extract the zip.
  2. Go to your Xcode project’s “General” settings. Drag Realm.framework from the ios/dynamic/, osx/, tvos/ or watchos/ directory to the “Embedded Binaries” section. Make sure Copy items if needed is selected (except if using Realm on multiple platforms in your project) and click Finish.
  3. In your unit test target’s “Build Settings”, add the parent path to Realm.framework in the “Framework Search Paths” section.
  4. 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.
  5. If using Realm in an iOS, watchOS or tvOS project, create a new “Run Script Phase” in your app’s target’s “Build Phases” and paste the following snippet in the script text field:

    bash "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Realm.framework/strip-frameworks.sh"

    This step is required to work around an App Store submission bug when archiving universal binaries.

  1. Install CocoaPods 1.1.0 or later.
  2. Run pod repo update to make CocoaPods aware of the latest available Realm versions.
  3. In your Podfile, add pod 'Realm' to your app target and pod 'Realm/Headers' to your test target.
  4. From the command line, run pod install.
  5. Use the .xcworkspace file generated by CocoaPods to work on your project!
  6. 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.
  1. Install Carthage 0.17.0 or later.
  2. Add github "realm/realm-cocoa" to your Cartfile.
  3. Run carthage update.
  4. Drag Realm.framework from the appropriate platform directory in Carthage/Build/ to the “Linked Frameworks and Libraries” section of your Xcode project’s “General” settings.

iOS/tvOS/watchOS: On your application target’s “Build Phases” settings tab, click the “+” icon and choose “New Run Script Phase”. Create a Run Script with the following contents:

/usr/local/bin/carthage copy-frameworks

and add the paths to the frameworks you want to use under “Input Files”, e.g.:

$(SRCROOT)/Carthage/Build/iOS/Realm.framework

This script works around an App Store submission bug triggered by universal binaries.

  1. Download the latest release of Realm and extract the zip.
  2. Drag Realm.framework from the ios/static/ directory to the File Navigator of your Xcode project. Make sure Copy items if needed is selected and click Finish.
  3. 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 click + and add libc++.tbd and libz.tbd.
  4. 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.

Realm Browser

You can use the standalone Realm Browser Mac application to read and edit .realm databases. (You can also download it from GitHub.)

Realm Browser

Create a test database with sample data using the menu item Tools > Generate demo database.

If you need help finding your app’s Realm file, check this StackOverflow answer for detailed instructions.

Examples

You can find example applications for both iOS and OS X in our release zip under examples/, demonstrating how to use many features of Realm like migrations, how to use it with UITableViewControllers, encryption, command-line tools and much more.

Using the Realm framework

At the top of your Objective‑C source files, use #import <Realm/Realm.h> to import Realm Objective‑C and make it available for use with your code. At the top of your Swift source files (if you have any), use import Realm. That’s all you need to get started!

Using Realm Objective‑C from Swift

If you’re looking to use Realm purely from Swift, you should consider using Realm Swift instead.

Realm Objective‑C is designed to work well with mixed Objective‑C and Swift projects. From Swift, you can do everything you can do when using Realm from Objective‑C, like defining models and using Realm’s Objective‑C API. However, there are a few things which you should do slightly differently than with pure Objective‑C projects:

RLMSupport.swift

We recommend you compile the Swift/RLMSupport.swift file (which is also available in our release zip). This file adds Sequence conformance to Realm Objective‑C collection types and re-exposes Objective‑C methods that aren’t natively accessible from Swift like methods including variadic arguments.

Realm Objective‑C doesn’t include this file by default, because that would force all users of Realm Objective‑C to include the hefty Swift dynamic libraries regardless of whether or not they use Swift in their app!

RLMArray properties

In Objective‑C, we rely on protocol conformance to make Realm aware of the contained object type in RLMArray to-many relationships. In Swift, this kind of syntax isn’t possible. So instead, you should declare your RLMArray properties with the following syntax:

class Person: Object {
    @objc dynamic var dogs = RLMArray(objectClassName: Dog.className())
}

which is equivalent to the following in Objective‑C:

@interface Person : RLMObject
@property RLMArray<Dog *><Dog> *dogs;
@end

tvOS

Because writing to the “Documents” directory is prohibited on tvOS, the default Realm location is set to NSCachesDirectory. However, please be mindful that tvOS can purge files in the “Caches” directory at any point, so we encourage you to rely on Realm as a rebuildable cache rather than storing important user data.

If you would like to share a Realm file between a tvOS app and a TV services extension (e.g. Top Shelf extension), you have to use the Library/Caches/ directory within the shared container for the application group.

// end declarations
RLMRealmConfiguration *configuration = [RLMRealmConfiguration defaultConfiguration];
configuration.fileURL = [[[NSFileManager defaultManager]
    containerURLForSecurityApplicationGroupIdentifier:@"group.io.realm.examples.extension"]
    URLByAppendingPathComponent:@"Library/Caches/default.realm"];

You can also bundle prebuilt Realm files in your app. However, be sure to comply with App Store guidelines, keeping your app under 200MB. Please browse our tvOS examples for sample tvOS apps demonstrating how to use Realm as either an offline cache or with preloaded data.

Using Realm with background app refresh

On iOS 8 and above, files inside apps are automatically encrypted using NSFileProtection whenever the device is locked. If your app attempts to do any work involving Realm while the device is locked and the NSFileProtection attributes of your Realm files are set to encrypt them (which is the case by default), an open() failed: Operation not permitted exception will be thrown.

In order to handle this, it is necessary to ensure that the file protection attributes applied to both the Realm file itself and its auxiliary files is downgraded to a less strict one that allows file access even when the device is locked, such as NSFileProtectionCompleteUntilFirstUserAuthentication.

If you choose to opt out of complete iOS file encryption in this way, we encourage you to use Realm’s own built-in encryption to ensure your data is still properly secured.

Since the auxiliary files can sometimes be lazily created and deleted mid-operation, we recommend that you apply the file protection attributes to the parent folder containing these Realm files. This will ensure the attribute is properly applied to all of the relevant Realm files, regardless of their creation time.

RLMRealm *realm = [RLMRealm defaultRealm];

// Get our Realm file's parent directory
NSString *folderPath = realm.configuration.fileURL.URLByDeletingLastPathComponent.path;

// Disable file protection for this directory
[[NSFileManager defaultManager] setAttributes:@{NSFileProtectionKey: NSFileProtectionNone}
                                 ofItemAtPath:folderPath error:nil];

Realms

A Realm is an instance of a Realm Mobile Database container. Realms can be local or synchronized.

A synchronized Realm uses the Realm Object Server to transparently synchronize its contents with other devices. In practice, your application works with any kind of Realm the same way, although opening a synchronized Realm requires a user that’s been authenticated to the Object Server and that’s authorized to open that Realm. While your application continues working with a synchronized Realm, the data in that Realm might be updated by any device with write access to that Realm.

For a detailed discussion about Realms, please read The Realm Data Model.

Opening Realms

To open a Realm, instantiate a new RLMRealm object:

RLMRealm *realm = [RLMRealm defaultRealm];

[realm transactionWithBlock:^{
    [realm addObject:mydog];
}];

This instantiates the default Realm.

Configuring a Realm

Configure a Realm before opening it by creating an instance of RLMRealmConfiguration and setting the appropriate properties. Creating and customizing a configuration value allows you to customize, among other aspects:

  • For a local Realm, the path on disk to the Realm file.
  • For a synchronized Realm, the user to whom the Realm belongs and the Realm’s remote path on the Realm Object Server.
  • For Realms whose schemas have changed between versions, a migration function controlling how the Realm’s data should be updated to the newest schema.
  • For Realms storing lots of data or frequently-changed data, a compaction function controlling how the Realm file should be compacted to ensure efficient utilization of disk space.

The configuration can either be passed to +[RLMRealm realmWithConfiguration:config error:&err] each time you need a Realm instance, or you can set the configuration to use for the default Realm with [RLMRealmConfiguration setDefaultConfiguration:config].

For example, suppose you have an application where users have to log in to your web backend, and you want to support quickly switching between accounts. You could give each account its own Realm file that will be used as the default Realm by doing the following:

@implementation SomeClass
+ (void)setDefaultRealmForUser:(NSString *)username {
    RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];

    // Use the default directory, but replace the filename with the username
    config.fileURL = [[[config.fileURL URLByDeletingLastPathComponent]
                            URLByAppendingPathComponent:username]
                            URLByAppendingPathExtension:@"realm"];

    // Set this as the configuration used for the default Realm
    [RLMRealmConfiguration setDefaultConfiguration:config];
}
@end

You can have multiple configuration objects, so you can control the version, schema and location of each Realm independently.

RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];

// Get the URL to the bundled file
config.fileURL = [[NSBundle mainBundle] URLForResource:@"MyBundledData" withExtension:@"realm"];
// Open the file in read-only mode as application bundles are not writeable
config.readOnly = YES;

// Open the Realm with the configuration
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];

// Read some data from the bundled Realm
RLMResults<Dog *> *dogs = [Dog objectsInRealm:realm where:@"age > 5"];

The most common location to store writable Realm files is the “Documents” directory on iOS and the “Application Support” directory on macOS. Please respect Apple’s iOS Data Storage Guidelines, which recommend that if documents that can be regenerated by the app should be stored in the <Application_Home>/Library/Caches directory. If a custom URL is used to initialize a Realm, it must describe a location with write permissions.

The default Realm

You may have noticed so far that we have initialized access to our realm variable by calling [RLMRealm defaultRealm]. That method returns an RLMRealm object that maps to a file named default.realm in the Documents folder (iOS) or Application Support folder (macOS) of your app.

Many methods in Realm’s API have both a version that accepts an RLMRealm instance, and a convenience version that uses the default Realm. For example, [RLMObject allObjects] is equivalent to [RLMObject allObjectsInRealm:[RLMRealm defaultRealm]].

Please note that the default Realm constructor and default Realm convenience methods don’t allow error handling; you should only use them when initializing the Realm cannot fail. See our documentation on error handling for details.

Opening a synchronized Realm

Realms on the Realm Object Server are using the same RLMRealmConfiguration and factory methods that are used to create standalone Realms, but with the syncConfiguration property on their RLMRealmConfiguration set to a RLMSyncConfiguration value. Synchronized realms are located by URLs.

RLMSyncUser *user = [RLMSyncUser currentUser];

// Create the configuration
NSURL *syncServerURL = [NSURL URLWithString: @"realm://localhost:9080/~/userRealm"];
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.syncConfiguration = [[RLMSyncConfiguration alloc] initWithUser:user realmURL:syncServerURL];

// Open the remote Realm
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
// Any changes made to this Realm will be synced across all devices!

If a Realm has read-only permissions, then you must use the asyncOpen API as described in Asynchronously opening Realms. Opening a read-only Realm without asyncOpen will cause an error.

The configuration values for a synced Realm cannot have an inMemoryIdentifier or fileURL configured. Setting either property will automatically nil out the syncConfiguration property (and vice-versa). The framework is responsible for managing how synchronized Realms are cached or stored on disk.

Asynchronously opening Realms

If opening a Realm requires a time-consuming operation, such as applying migrations, compaction or downloading the remote contents of a synchronized Realm, you should use the asyncOpen API. This lets you do any initialization work on a background thread before dispatching to the given queue. You should also use asyncOpen with synchronized Realms when they are opened with read-only permissions.

RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.schemaVersion = 1;
config.migrationBlock = ^(RLMMigration *migration, uint64_t oldSchemaVersion) {
    // potentially lengthy data migration
};
[RLMRealm asyncOpenWithConfiguration:config
                       callbackQueue:dispatch_get_main_queue()
                            callback:^(RLMRealm *realm, NSError *error) {
    if (realm) {
        // Realm successfully opened, with migration applied on background thread
    } else if (error) {
        // Handle error that occurred while opening the Realm
    }
}];

Initial downloads

In some cases, you might not want to open a Realm until it has all remote data available. For example, if you want to show the users a list of all available ZIP codes. This is another use case for the asyncOpen API. This downloads the Realm in the background before reporting it as ready:

RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.syncConfiguration = [[RLMSyncConfiguration alloc] initWithUser:user realmURL:realmURL];
[RLMRealm asyncOpenWithConfiguration:config
                       callbackQueue:dispatch_get_main_queue()
                            callback:^(RLMRealm *realm, NSError *error) {
    if (realm) {
        // Realm successfully opened, with all remote data available
    } else if (error) {
        // Handle error that occurred while opening or downloading the contents of the Realm
    }
}];

In-memory Realms

By setting the inMemoryIdentifier rather than the fileURL on your RLMRealmConfiguration, you can create a Realm that runs entirely in memory without being persisted to disk. Setting inMemoryIdentifier will nil out fileURL (and vice-versa).

RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.inMemoryIdentifier = @"MyInMemoryRealm";
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];

In-memory Realms do not save data across app launches, but 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.

In-memory Realms create several files in a temporary directory for coordinating things like cross-process notifications. No data is actually written to the files unless the operating system needs to swap to disk due to memory pressure.

Notice: When all in-memory Realm instances with a particular identifier go out of scope with no references, all data in that Realm is deleted. We recommend holding onto a strong reference to any in-memory Realms during your app’s lifetime. (This is not necessary for on-disk Realms.)

Error handling

Like any disk I/O operation, creating a RLMRealm instance could sometimes fail if resources are constrained. In practice, this can only happen the first time a Realm instance is created on a given thread. Subsequent accesses to a Realm from the same thread will reuse a cached instance and will always succeed.

To handle errors when first accessing a Realm on a given thread, provide an NSError pointer to the error parameter:

NSError *error = nil;

RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:&error];
if (!realm) {
    // handle error
}

Auxiliary Realm files

Alongside the standard .realm files, Realm also generates and maintains additional files and directories for its own internal operations.

  • .realm.lock - A lock file for resource locks.
  • .realm.management - Directory of interprocess lock files.
  • .realm.note - A named pipe for notifications.

These files don’t have any effect on .realm database files, and won’t cause any erroneous behavior if their parent database file is deleted or replaced.

When reporting Realm issues, please be sure to include these auxiliary files along with your main .realm file as they contain useful information for debugging purposes.

Bundling a Realm

It’s common to seed an app with initial data, making it available to your users immediately on first launch. Here’s how to do this:

  1. First, populate the Realm. You should use the same data model as your final, shipping app to create a Realm and populate it with the data you wish to bundle with your app. Since Realm files are cross-platform, you can use a macOS app (see our JSONImport example) or your iOS app running in the simulator.
  2. In the code where you’re generating this Realm file, you should finish by making a compacted copy of the file (see -[RLMRealm writeCopyToPath:error:]). This will reduce the Realm’s file size, making your final app lighter to download for your users.
  3. Drag the new compacted copy of your Realm file to your final app’s Xcode Project Navigator.
  4. Go to your app target’s build phases tab in Xcode and add the Realm file to the “Copy Bundle Resources” build phase.
  5. At this point, your bundled Realm file will be accessible to your app. You can find its path by using [[NSBundle mainBundle] pathForResource:ofType:].
  6. If the bundled Realm contains fixed data that you don’t need to modify, you can open it directly from the bundle path by setting readOnly = true on the RLMRealmConfiguration object. Otherwise, if it’s initial data that you’ll be modifying, you can copy the bundled file to your application’s Documents directory using [[NSFileManager defaultManager] copyItemAtPath:toPath:error:].

You can refer to our migration sample app for an example of how to use a bundled Realm file.

Class subsets

In some scenarios you may wish to limit which classes can be stored in a specific Realm. For example, if you have two teams working on different components of your application which both use Realm internally, you may not want to have to coordinate migrations between them. You can do this by setting the objectClasses property of your RLMRealmConfiguration:

RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.objectClasses = @[MyClass.class, MyOtherClass.class];
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];

Compacting Realms

Realm works in such a way that the size of a Realm file is always larger than the total size of the objects stored within it. See our documentation on threading for some of the reasons why this architecture enables some of Realm’s great performance, concurrency and safety advantages.

In order to avoid making expensive system calls, Realm files are rarely shrunk at runtime. Instead, they grow by specific size increments, with new data being written within unused space tracked inside the file. However, there may be situations in which a significant portion of a Realm file is comprised of unused space. In order to deal with this, you may set the shouldCompactOnLaunch block property upon a Realm’s configuration object to determine if, when opened for the first time, the Realm file should be compacted. For example:

RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.shouldCompactOnLaunch = ^BOOL(NSUInteger totalBytes, NSUInteger usedBytes){
    // totalBytes refers to the size of the file on disk in bytes (data + free space)
    // usedBytes refers to the number of bytes used by data in the file

    // Compact if the file is over 100MB in size and less than 50% 'used'
    NSUInteger oneHundredMB = 100 * 1024 * 1024;
    return (totalBytes > oneHundredMB) && (usedBytes / totalBytes) < 0.5;
};

NSError *error = nil;
// Realm is compacted on the first open if the configuration block conditions were met.
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:&error];
if (error) {
    // handle error compacting or opening Realm
}

The compaction operation works by reading the entire contents of the Realm file, rewriting it to a new file at a different location, then replacing the original file. Depending on the amount of data in a file, this may be an expensive operation.

We encourage you to experiment with the numbers to identify a good balance between performing the compaction too often and letting Realm files grow too large.

Finally, if another process is accessing the Realm, compaction will be skipped even if the configuration block’s conditions were met. That’s because compaction cannot be safely performed while a Realm is being accessed.

Setting a shouldCompactOnLaunch block is not supported for synchronized Realms. This is because compaction doesn’t preserve transaction logs, which must be kept for synchronization.

Deleting Realm files

In some cases, such as clearing caches, or resetting your entire dataset, it may be appropriate to completely delete a Realm file from disk.

Because Realm avoids copying data into memory except when absolutely required, all objects managed by a Realm contain references to the file on disk, and must be deallocated before the file can be safely deleted. This includes all objects read from (or added to) the Realm, all RLMArray, RLMResults, and RLMThreadSafeReference objects, and the RLMRealm itself.

In practice, this means that deleting a Realm file should be done either on application startup before you have opened the Realm, or after only opening the Realm within an explicit autorelease pool, which ensures that all of the Realm objects will have been deallocated.

Finally, although not strictly necessary, you should delete auxiliary Realm files as well as the main Realm file to fully clean up all related files.

@autoreleasepool {
    // all Realm usage here
}
NSFileManager *manager = [NSFileManager defaultManager];
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
NSArray<NSURL *> *realmFileURLs = @[
    config.fileURL,
    [config.fileURL URLByAppendingPathExtension:@"lock"],
    [config.fileURL URLByAppendingPathExtension:@"note"],
    [config.fileURL URLByAppendingPathExtension:@"management"]
];
for (NSURL *URL in realmFileURLs) {
    NSError *error = nil;
    [manager removeItemAtURL:URL error:&error];
    if (error) {
        // handle error
    }
}

Models

Realm data models are defined as regular Objective‑C classes with regular properties. To create one, simply subclass RLMObject or an existing Realm model class. Realm model objects mostly function like any other Objective‑C objects. You can define your own methods on them, conform them to protocols, and use them like you would any other object. The main restrictions are that you can only use an object on the thread which it was created, and you can’t access its ivars directly for any persisted properties.

Relationships and nested data structures are modeled by including properties of the target type or RLMArrays for typed lists of objects. RLMArray instances can also be used to model collections of primitive values (for example, an array of strings or integers).

#import <Realm/Realm.h>

@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 *><Dog> *dogs;
@end
RLM_ARRAY_TYPE(Person) // define RLMArray<Person>

// Implementations
@implementation Dog
@end // none needed

@implementation Person
@end // none needed

Since Realm parses all models defined in your code at launch, they must all be valid, even if they are never used.

When using Realm from Swift, the Swift.reflect(_:) function is used to determine information about your models, which requires that calling init() succeed. This means that all non-optional properties must have a default value.

See our API documentation on RLMObject for more details.

Supported property types

Realm supports the following property types: BOOL, bool, int, NSInteger, long, long long, float, double, NSString, NSDate, NSData, and NSNumber tagged with a specific type.

CGFloat properties are discouraged, as the type is not platform independent.

You can use RLMArray<Object *><Object> and RLMObject subclasses to model relationships such as to-many and to-one.

RLMArrays support Objective‑C generics. Here’s what the different components of the property definition mean and why they are useful:

  • RLMArray: The property type.
  • <Object *>: The generic specialization. This helps prevent using the array with the wrong object type at compile-time.
  • <Object>: The protocol this RLMArray conforms to. This enables Realm to know how to specialize the schema of this model at runtime.

Required properties

By default, NSString *, NSData *, and NSDate * properties allow you to set them to nil. If you want to require that a value be present, override the +requiredProperties method on your RLMObject subclass.

For example, with the following model definition, trying to set the person’s name to nil will throw an exception, but setting their birthday to nil is allowed:

@interface Person : RLMObject
@property NSString *name;
@property NSDate *birthday;
@end

@implementation Person
+ (NSArray *)requiredProperties {
    return @[@"name"];
}
@end

Storing optional numbers is done using NSNumber * properties. Because Realm uses different storage formats for different types of numbers, the property must be tagged with one of RLMInt, RLMFloat, RLMDouble, or RLMBool. All values assigned to the property will be converted to the specified type.

Note that NSDecimalNumber values can only be assigned to RLMDouble Realm properties, and that Realm will store the double-precision floating point approximation of the value, not the underlying decimal value.

If we wanted to store someone’s age rather than their birthday, while still allowing nil when their age is unknown:

@interface Person : RLMObject
@property NSString *name;
@property NSNumber<RLMInt> *age;
@end

@implementation Person
+ (NSArray *)requiredProperties {
    return @[@"name"];
}
@end

RLMObject subclass properties always can be nil, and thus cannot be included in requiredProperties. and RLMArray does not support storing nil.

Primary keys

Override +primaryKey to set the model’s primary key. Declaring a primary key allows objects to be looked up and updated efficiently and enforces uniqueness for each value. Once an object with a primary key is added to a Realm, the primary key cannot be changed.

@interface Person : RLMObject
@property NSInteger id;
@property NSString *name;
@end

@implementation Person
+ (NSString *)primaryKey {
    return @"id";
}
@end

Indexing properties

To index a property, override +indexedProperties. Like primary keys, indexes make writes slightly slower, but makes queries using comparison operators faster. (It also makes your Realm file slightly larger, to store the index.) It’s best to only add indexes when you’re optimizing the read performance for specific situations.

@interface Book : RLMObject
@property float price;
@property NSString *title;
@end

@implementation Book
+ (NSArray *)indexedProperties {
    return @[@"title"];
}
@end

Realm supports indexing for string, integer, boolean, and NSDate properties.

Ignoring properties

If you don’t want to save a field in your model to its Realm, override +ignoredProperties. Realm won’t interfere with the regular operation of these properties; they’ll be backed by ivars, and you can freely override their setters and getters.

@interface Person : RLMObject
@property NSInteger tmpID;
@property (readonly) NSString *name; // read-only properties are automatically ignored
@property NSString *firstName;
@property NSString *lastName;
@end

@implementation Person
+ (NSArray *)ignoredProperties {
    return @[@"tmpID"];
}
- (NSString *)name {
    return [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];
}
@end

Ignored properties behave exactly like normal properties. They don’t support any Realm-specific functionality (e.g., they can’t be used in queries and won’t trigger notifications). They can still be observed using KVO.

Default property values

Override +defaultPropertyValues to provide default values every time an object is created.

@interface Book : RLMObject
@property float price;
@property NSString *title;
@end

@implementation Book
+ (NSDictionary *)defaultPropertyValues {
    return @{@"price" : @0, @"title": @""};
}
@end

Property attributes

Realm ignores Objective‑C property attributes like nonatomic, atomic, strong, copy, weak, etc. These aren’t meaningful for Realm storage; it has its own optimized storage semantics. So to avoid misleading anyone reading your code, we recommend writing models without any property attributed at all. However, if you do set property attributes, they will be used until an RLMObject is added to a Realm.

Custom names for getters and setters work normally regardless of whether or not an RLMObject is managed by a Realm.

Because unmanaged Realm objects (instances of Realm model classes that aren’t managed by a Realm) are just plain old NSObjects, property attributes are observed just like any other NSObject.

If using Realm Objective‑C with Swift, model properties need the @objc dynamic var attribute in order for these properties to become accessors for the underlying database data. (You can also declare the class with @objcMembers and declare model properties with dynamic var.)

Property cheatsheet

This table provides a handy reference to declaring model properties.

Type Non-optional Optional
Bool @property BOOL value; @property NSNumber<RLMBool> *value;
Int @property int value; @property NSNumber<RLMInt> *value;
Float @property float value; @property NSNumber<RLMFloat> *value;
Double @property double value; @property NSNumber<RLMDouble> *value;
String @property NSString *value; 1 @property NSString *value;
Data @property NSData *value; 1 @property NSData *value;
Date @property NSDate *value; 1 @property NSDate *value;
Object n/a: must be optional @property Object *value;
List @property RLMArray<Class *><Class> *value; n/a: must be non-optional
LinkingObjects @property (readonly) RLMLinkingObjects<Object *> *value; 2 n/a: must be non-optional

Required properties of an Objective‑C reference type have to be declared in combination with an overriden implementation of the +requiredProperties method:

@implementation MyModel
+ (NSArray *)requiredProperties {
    // The array must contain the names of all required properties.
    return @[@"value"];
}
@end

Working with Realm objects

Auto-updating objects

RLMObject instances are live, auto-updating views into the underlying data; you never have to refresh objects. Modifying the properties of an object will immediately be reflected in any other instances referring to the same object.

Dog *myDog = [[Dog alloc] init];
myDog.name = @"Fido";
myDog.age = 1;

[realm transactionWithBlock:^{
    [realm addObject:myDog];
}];

Dog *myPuppy = [[Dog objectsWhere:@"age == 1"] firstObject];
[realm transactionWithBlock:^{
    myPuppy.age = 2;
}];

myDog.age; // => 2

This not only keeps Realm fast and efficient, it allows your code to be simpler and more reactive. If your UI code is dependent on a specific Realm object, you don’t need to worry about refreshing or re-fetching it before triggering a UI redraw.

You can subscribe to Realm notifications to know when Realm data in an object is updated, indicating when your app’s UI should be refreshed.

Model inheritance

Realm allows models to be further subclassed, allowing for code reuse across models, but some Cocoa features that contribute to the runtime’s rich class polymorphism aren’t available. Here’s what’s possible:

  • Class methods, instance methods, and properties on parent classes are inherited in their child classes.
  • Methods and functions that take parent classes as arguments can operate on subclasses.

The following is currently not possible:

  • Casting between polymorphic classes (ie, subclass to subclass, subclass to parent, parent to subclass, etc.)
  • Querying on multiple classes simultaneously
  • Multi-class containers (RLMArray and RLMResults)

Adding this functionality to Realm is on the roadmap. For the time being, we’ve provided some code samples for working around some of the more common patterns.

Alternatively, if your implementation allows it, we recommend using the following pattern of class composition to build up subclasses encompassing logic from other classes:

// Base Model
@interface Animal : RLMObject
@property NSInteger age;
@end
@implementation Animal
@end

// Models composed with Animal
@interface Duck : RLMObject
@property Animal *animal;
@property NSString *name;
@end
@implementation Duck
@end

@interface Frog : RLMObject
@property Animal *animal;
@property NSDate *dateProp;
@end
@implementation Frog
@end

// Usage
Duck *duck =  [[Duck alloc] initWithValue:@{@"animal" : @{@"age" : @(3)}, @"name" : @"Gustav" }];

Collections

Realm has several types that help represent groups of objects, which we refer to as “Realm collections”:

  1. RLMResults, a class representing objects retrieved from queries.
  2. RLMArray, a class representing to-many relationships in models.
  3. RLMLinkingObjects, a class representing inverse relationships in models.
  4. RLMCollection, a protocol defining the common interface to which all Realm collections conform.

The Realm collection types each conform to the RLMCollection protocol, which ensures they behave consistently. This protocol inherits from NSFastEnumeration so that it may be used in the same ways as other Foundation collections. Additional common Realm collection APIs are declared in this protocol, such as querying, sorting and aggregate operations, among others. RLMArrays have additional mutation operations that extend beyond the protocol interface such as adding and deleting objects or values.

Using the RLMCollection protocol, you can write generic code that can operate on any Realm collection:

@implementation MyObject
- (void)operateOnCollection:(id<RLMCollection>)collection {
    // Collection could be either RLMResults or RLMArray
    NSLog(@"operating on collection of %@s", collection.objectClassName);
}
@end

Copying objects between Realms

Copying Realm objects to other Realms is as simple as passing in the original object to +[RLMObject createInRealm:withValue:]. For example, [MyRLMObjectSubclass createInRealm:otherRealm withValue:originalObjectInstance]. Remember that Realm objects can only be accessed from the thread on which they were first created, so this copy will only work for Realms on the same thread.

Note that +[RLMObject createInRealm:withValue:] does not support handling cyclical object graphs. Do not pass in an object containing relationships involving objects that refer back to their parents, either directly or indirectly.

Relationships

You can link any two Realm Objects together. Relationships are cheap in Realm: traversing links isn’t expensive in terms of speed or memory. Let’s explore the different types of relationships Realm lets you define between objects.

Link a RLMObject by using RLMObject and RLMArray properties. RLMArrays have an interface very similar to NSArray, and objects contained in a RLMArray can be accessed using indexed subscripting. Unlike NSArrays, RLMArrays are typed and only hold RLMObjects of a single subclass type. For more details, see the API documentation for RLMArray.

Assuming your Person model has already been defined (see models), let’s create another model called Dog:

// Dog.h
@interface Dog : RLMObject
@property NSString *name;
@end

Many-to-one

To set up a many-to-one or one-to-one relationship, give a model a property whose type is one of your RLMObject subclasses:

// Dog.h
@interface Dog : RLMObject
// ... other property declarations
@property Person *owner;
@end

You can use this property like you would any other:

Person *jim = [[Person alloc] init];
Dog    *rex = [[Dog alloc] init];
rex.owner = jim;

When using RLMObject properties, you can access nested properties using normal property syntax. For example, rex.owner.address.country will traverse the object graph and automatically fetch each object from Realm as needed.

Many-to-many

You can can create a relationship to any number of objects or supported primitive values using RLMArray properties. RLMArrays contain other RLMObjects or primitive values of a single type and have an interface very similar to NSMutableArray.

RLMArrays containing Realm objects may store multiple references to the same Realm object, including objects with primary keys. For example, you may create an empty RLMArray and insert the same object into it three times; the RLMArray will then return that object if the element at any of the indices 0, 1, and 2 is accessed.

RLMArrays can store primitive values in lieu of Realm objects. In order to do so, constrain a RLMArray with one of the following protocols: RLMBool, RLMInt, RLMFloat, RLMDouble, RLMString, RLMData, or RLMDate.

By default, RLMArrays containing primitive types may contain null values (indicated by NSNull). Indicating that the array is required (by overriding +requiredProperties: on the model object type the array belongs to) will make the values in the array required as well.

Let’s add a dogs property on our Person model that links to multiple dogs. First, we define an RLMArray<Dog> type, using a macro at the bottom of the Dog model interface:

// Dog.h
@interface Dog : RLMObject
// ... property declarations
@end

RLM_ARRAY_TYPE(Dog) // Defines an RLMArray<Dog> type

The RLM_ARRAY_TYPE macro creates an protocol to enable the use of the RLMArray<Dog> syntax. You may have to forward-declare the model class if the macro is not placed at the bottom of the model interface.

You can then declare properties of the RLMArray<Dog> type:

// Person.h
@interface Person : RLMObject
// ... other property declarations
@property RLMArray<Dog *><Dog> *dogs;
@end

You can access and assign RLMArray properties as usual:

// Jim is owner of Rex and all dogs named "Fido"
RLMResults<Dog *> *someDogs = [Dog objectsWhere:@"name contains 'Fido'"];
[jim.dogs addObjects:someDogs];
[jim.dogs addObject:rex];

Note that although it’s possible to assign nil to an RLMArray property, this only “empties” the array rather than removing the array. This means that you can always add objects to an RLMArray property, even after setting it to nil.

RLMArray properties are guaranteed to preserve their order of insertion.

Note that querying RLMArrays containing primitive values is currently not supported.

Inverse relationships

Relationships are unidirectional. Take our two classes Person and Dog as an example. If Person.dogs links to a Dog instance, you can follow the link from Person to a Dog, but there’s no way to go from a Dog to its Person objects. You can set a one-to-one property Dog.owner linking to Person, but those links are independent from one another. Adding a Dog to Person.dogs won’t set that dog’s Dog.owner property to the correct Person. To solve this problem, Realm provides linking objects properties to represent inverse relationships.

@interface Dog : RLMObject
@property NSString *name;
@property NSInteger age;
@property (readonly) RLMLinkingObjects *owners;
@end

@implementation Dog
+ (NSDictionary *)linkingObjectsProperties {
    return @{
        @"owners": [RLMPropertyDescriptor descriptorWithClass:Person.class propertyName:@"dogs"],
    };
}
@end

With linking objects properties, you can obtain all objects that link to a given object from a specific property. A Dog object can have a property named owners that contains all of the Person objects that have this exact Dog object in their dogs property. Make the owners property of type RLMLinkingObjects and then overriding +[RLMObject linkingObjectsProperties] to indicate the relationship that owners has with the Person model objects.

Writes

All changes to an object (addition, modification and deletion) must be done within a write transaction.

Realm objects can be instantiated and used as unmanaged objects (i.e. not yet added to a Realm) just like regular Objective‑C objects. To share objects between threads or re-use them between app launches, however, you must add them to a Realm. Adding an object to a Realm must be done within a write transaction. Since write transactions incur non-negligible overhead, you should architect your code to minimize the number of write transactions.

Realm write operations are synchronous and blocking, not asynchronous. If thread A starts a write operation, then thread B starts a write operation on the same Realm before thread A is finished, thread A must finish and commit its transaction before thread B’s write operation takes place. Write operations always refresh automatically on beginWrite(), so no race condition is created by overlapping writes.

Because write transactions could potentially fail like any other disk IO operations, both -[RLMRealm transactionWithBlock:] & -[RLMRealm commitWriteTransaction] optionally take an NSError pointer parameter so you can handle and recover from failures like running out of disk space. There are no other recoverable errors. For brevity, our code samples don’t handle these errors, but you certainly should in your production applications.

Creating objects

When you have defined a model you can instantiate your RLMObject subclass and add the new instance to the Realm. Consider this simple model:

// Dog model
@interface Dog : RLMObject
@property NSString *name;
@property NSInteger age;
@end

// Implementation
@implementation Dog
@end

We can create new objects in several ways:

// (1) Create a Dog object and then set its properties
Dog *myDog = [[Dog alloc] init];
myDog.name = @"Rex";
myDog.age = 10;

// (2) Create a Dog object from a dictionary
Dog *myOtherDog = [[Dog alloc] initWithValue:@{@"name" : @"Pluto", @"age" : @3}];

// (3) Create a Dog object from an array
Dog *myThirdDog = [[Dog alloc] initWithValue:@[@"Pluto", @3]];
  1. The most obvious is to use the designated initializer to create an object. Please note that all required properties must be set before an object can be added to the Realm.
  2. Objects can also be created from dictionaries using appropriate keys and values.
  3. Finally, RLMObject subclasses can be instantiated using arrays. The values in the array have to be in the same order as the corresponding properties in the model.

Values in an array should correspond to properties stored in the Realm—you shouldn’t specify values for ignored properties, or computed properties.

After the object is created, you can add it to a Realm:

// 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:myDog];
[realm commitWriteTransaction];

After you have added the object to the Realm you can continue using it, and all changes you make to it will be persisted (and must be made within a write transaction). Any changes are made available to other threads that use the same Realm when the write transaction is committed.

Please note that writes block each other, and will block the thread they are made on if multiple writes are in progress. This is similar to other persistence solutions and we recommend that you use the usual best practice for this situation: offloading your writes to a separate thread.

Thanks to Realm’s MVCC architecture, reads are not blocked while a write transaction is open. 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. When you commit a write transaction to a Realm, all other instances of that Realm will be notified, and be updated automatically.

See RLMRealm and RLMObject for more details.

Nested objects

If an object has properties that are RLMObjects or RLMArrays, these can be set recursively using nested arrays and/or dictionaries. You simply replace each object with a dictionary or array representing its properties:

// Instead of using already existing dogs...
Person *person1 = [[Person alloc] initWithValue:@[@"Jane", @30, @[aDog, anotherDog]]];

// ...we can create them inline
Person *person2 = [[Person alloc] initWithValue:@[@"Jane", @30, @[@[@"Buster", @5],
                                                                  @[@"Buddy", @6]]]];

This will work for any combination of nested arrays and dictionaries. Note that an RLMArray may only contain RLMObjects, not basic types such as NSString.

Updating objects

Realm provides a few ways to update objects, all of which offer different tradeoffs depending on the situation.

Typed updates

You can update any object by setting its properties within a write transaction.

// Update an object with a transaction
[realm beginWriteTransaction];
author.name = @"Thomas Pynchon";
[realm commitWriteTransaction];

Key-value coding

RLMObject, RLMResult, and RLMArray all conform to key-value coding (KVC). This can be useful when you need to determine which property to update at runtime.

Applying KVC to a collection is a great way to update objects in bulk without the overhead of iterating over a collection while creating accessors for every item.

RLMResults<Person *> *persons = [Person allObjects];
[[RLMRealm defaultRealm] transactionWithBlock:^{
    [[persons firstObject] setValue:@YES forKeyPath:@"isFirst"];
    // set each person's planet property to "Earth"
    [persons setValue:@"Earth" forKeyPath:@"planet"];
}];

Objects with primary keys

If your model class includes a primary key, you can have Realm intelligently update or add objects based off of their primary key values using -[RLMRealm addOrUpdateObject:].

// Creating a book with the same primary key as a previously saved book
Book *cheeseBook = [[Book alloc] init];
cheeseBook.title = @"Cheese recipes";
cheeseBook.price = @9000;
cheeseBook.id = @1;

// Updating book with id = 1
[realm beginWriteTransaction];
[realm addOrUpdateObject:cheeseBook];
[realm commitWriteTransaction];

If a Book object with a primary key value of ‘1’ already existed in the database, then that object would simply be updated. If it did not exist, then a completely new Book object would be created and added to the database.

You can also partially update objects with primary keys by passing just a subset of the values you wish to update, along with the primary key:

// Assuming a "Book" with a primary key of `1` already exists.
[realm beginWriteTransaction];
[Book createOrUpdateInRealm:realm withValue:@{@"id": @1, @"price": @9000.0f}];
// the book's `title` property will remain unchanged.
[realm commitWriteTransaction];

You may not call those methods shown in this chapter (which end in OrUpdate) with or on objects which don’t define a primary key.

Please note that when updating objects, NSNull is still considered a valid value for optional properties. If you supply a dictionary with NSNull property values, then these will be applied to your object and those properties will be emptied. To ensure you don’t experience any unplanned data loss, please make sure to provide only the properties you wish to update when using this method.

Deleting objects

Pass the object to be deleted to the -[RLMRealm deleteObject:] method within a write transaction.

// cheeseBook stored in Realm

// Delete an object with a transaction
[realm beginWriteTransaction];
[realm deleteObject:cheeseBook];
[realm commitWriteTransaction];

You can also delete all objects stored in a Realm. Note the Realm file will maintain its size on disk to efficiently reuse that space for future objects.

// Delete all objects from the realm
[realm beginWriteTransaction];
[realm deleteAllObjects];
[realm commitWriteTransaction];

Queries

Queries return an RLMResults instance, which contains a collection of RLMObjects. RLMResults have an interface very similar to NSArray and objects contained in a RLMResults can be accessed using indexed subscripting. Unlike NSArrays, RLMResults are typed and only hold RLMObjects of a single subclass type.

All queries (including queries and property access) are lazy in Realm. Data is only read when the properties are accessed.

Results to a query are not copies of your data: modifying the results of a query (within a write transaction) will modify the data on disk directly. Similarly, you can traverse your graph of relationships directly from the RLMObjects contained in a RLMResults.

Execution of a query is deferred until the results are used. This means that chaining several temporary RLMResults to sort and filter your data does not perform extra work processing the intermediate state.

Once the query has been executed, or a notification block has been added, the RLMResults is kept up to date with changes made in the Realm, with the query execution performed on a background thread when possible.

The most basic method for retrieving objects from a Realm is +[RLMObject allObjects], which returns a RLMResults of all RLMObject instances of the subclass type being queried from the default Realm.

RLMResults<Dog *> *dogs = [Dog allObjects]; // retrieves all Dogs from the default Realm

Filtering

If you’re familiar with NSPredicate, then you already know how to query in Realm. RLMObjects, RLMRealm, RLMArray, and RLMResults all provide methods that allow you to query for specific RLMObject instances by simply passing in an NSPredicate instance, predicate string, or predicate format string just as you would when querying an NSArray.

For example, the following would extend our earlier example by calling [RLMObject objectsWhere:] to retrieve all tan-colored dogs whose names begin with ‘B’ from the default Realm:

// Query using a predicate string
RLMResults<Dog *> *tanDogs = [Dog objectsWhere:@"color = 'tan' AND name BEGINSWITH 'B'"];

// Query using an NSPredicate
NSPredicate *pred = [NSPredicate predicateWithFormat:@"color = %@ AND name BEGINSWITH %@",
                                                     @"tan", @"B"];
tanDogs = [Dog objectsWithPredicate:pred];

See Apple’s Predicates Programming Guide for more information about building predicates and use our NSPredicate Cheatsheet. Realm supports many common predicates:

  • 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, long long, float, double, and NSDate property types, e.g. age == 45
  • Identity comparisons ==, !=, e.g. [Employee objectsWhere:@"company == %@", company].
  • The comparison operators == and != are supported for boolean properties.
  • For NSString and NSData properties, the ==, !=, BEGINSWITH, CONTAINS, and ENDSWITH operators are supported, e.g. name CONTAINS 'Ja'
  • For NSString properties, the LIKE operator may be used to compare the left hand property with the right hand expression: ? and * are allowed as wildcard characters, where ? matches 1 character and * matches 0 or more characters. Example: value LIKE '?bc*' matching strings like “abcde” and “cbc”.
  • Case-insensitive comparisons for strings, such as name CONTAINS[c] 'Ja'. Note that only characters “A-Z” and “a-z” will be ignored for case. The [c] modifier can be combined with the [d]` modifier.
  • Diacritic-insensitive comparisons for strings, such as name BEGINSWITH[d] 'e' matching étoile. This modifier can be combined with the [c] modifier. (This modifier can only be applied to a subset of strings Realm supports: see limitations for details.)
  • Realm supports the following compound operators: “AND”, “OR”, and “NOT”, e.g. name BEGINSWITH 'J' AND age >= 32.
  • The containment operand IN, e.g. name IN {'Lisa', 'Spike', 'Hachi'}
  • Nil comparisons ==, !=, e.g. [Company objectsWhere:@"ceo == nil"]. Note that Realm treats nil as a special value rather than the absence of a value; unlike with SQL, nil equals itself.
  • ANY comparisons, e.g. ANY student.age < 21.
  • The aggregate expressions @count, @min, @max, @sum and @avg are supported on RLMArray and RLMResults properties, e.g. [Company objectsWhere:@"employees.@count > 5"] to find all companies with more than five employees.
  • Subqueries are supported with the following limitations:
    • @count is the only operator that may be applied to the SUBQUERY expression.
    • The SUBQUERY(…).@count expression must be compared with a constant.
    • Correlated subqueries are not yet supported.

See [RLMObject objectsWhere:].

Sorting

RLMResults allows you to specify a sort criteria and order based on a key path, a property, or on one or more sort descriptors. For example, the following calls sorts the dogs returned from the example above alphabetically by name:

// Sort tan dogs with names starting with "B" by name
RLMResults<Dog *> *sortedDogs = [[Dog objectsWhere:@"color = 'tan' AND name BEGINSWITH 'B'"]
                                    sortedResultsUsingKeyPath:@"name" ascending:YES];

The key path may also be the property of a to-one relationship:

RLMResults<Person *> *dogOwners = [Person allObjects];
RLMResults<Person *> *ownersByDogAge = [dogOwners sortedResultsUsingKeyPath:@"dog.age" ascending:YES];

Note that sortedResultsUsingKeyPath: and sortedResultsUsingProperty: do not support multiple properties as sort criteria, and cannot be chained (only the last call to sortedResults... will be used). To sort by multiple properties, use sortedResultsUsingDescriptors: with multiple RLMSortDescriptor objects.

For more, see:

Note that the order of Results is only guaranteed to stay consistent when the query is sorted. For performance reasons, insertion order is not guaranteed to be preserved. If you need to maintain order of insertion, some solutions are proposed here.

Chaining queries

One unique property of Realm’s query engine is the ability to chain queries with very little transactional overhead when compared to traditional databases that require a separate trip to the database server for each successive query.

If you wanted a result set for just tan colored dogs, and tan colored dogs whose names also start with ‘B’, you might chain two queries like this:

RLMResults<Dog *> *tanDogs = [Dog objectsWhere:@"color = 'tan'"];
RLMResults<Dog *> *tanDogsWithBNames = [tanDogs objectsWhere:@"name BEGINSWITH 'B'"];

Auto-updating results

RLMResults instances are live, auto-updating views into the underlying data, which means results never have to be re-fetched. They always reflect the current state of the Realm on the current thread, including during write transactions on the current thread. The one exception to this is when using for...in enumeration, which will always enumerate over the objects which matched the query when the enumeration is begun, even if some of them are deleted or modified to be excluded by the filter during the enumeration.

RLMResults<Dog *> *puppies = [Dog objectsInRealm:realm where:@"age < 2"];
puppies.count; // => 0

[realm transactionWithBlock:^{
    [Dog createInRealm:realm withValue:@{@"name": @"Fido", @"age": @1}];
}];

puppies.count; // => 1

This applies to all RLMResults: all objects, filtered and chained.

This property of RLMResults not only keeps Realm fast and efficient, it allows your code to be simpler and more reactive. For example, if your view controller relies on the results of a query, you can store the RLMResults in a property and access it without having to make sure to refresh its data prior to each access.

You can subscribe to Realm notifications to know when Realm data is updated, indicating when your app’s UI should be refreshed for example, without having to re-fetch your RLMResults.

Since results are auto-updating, it’s important to not rely on indices and counts staying constant. The only time a RLMResults is frozen is when fast-enumerating over it, which makes it possible to mutate the objects matching a query while enumerating over it:

[realm beginWriteTransaction];
for (Person *person in [Person objectsInRealm:realm where:@"age == 10"]) {
    person.age++;
}
[realm commitWriteTransaction];

Alternatively, use key-value coding to perform operations on RLMResults.

Limiting results

Most other database technologies provide the ability to ‘paginate’ results from queries (such as the ‘LIMIT’ keyword in SQLite). This is often done out of necessity to avoid reading too much from disk, or pulling too many results into memory at once.

Since queries in Realm are lazy, performing this sort of paginating behavior isn’t necessary at all, as Realm will only load objects from the results of the query once they are explicitly accessed.

If for UI-related or other implementation reasons you require a specific subset of objects from a query, it’s as simple as taking the RLMResults object, and reading out only the objects you need.

// Loop through the first 5 Dog objects
// restricting the number of objects read from disk
RLMResults<Dog *> *dogs = [Dog allObjects];
for (NSInteger i = 0; i < 5; i++) {
    Dog *dog = dogs[i];
    // ...
}

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 Objective‑C classes, making model changes is as easy as changing any other Objective‑C class.

Suppose we have the following Person model:

@interface Person : RLMObject
@property NSString *firstName;
@property NSString *lastName;
@property int age;
@end

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 object interface to the following:

@interface Person : RLMObject
@property NSString *fullName;
@property int age;
@end

At this point if you had saved any data with the previous model version, there will be a mismatch between what Realm sees defined in code and the data Realm sees on disk. When this occurs, an exception will be thrown when you try to open the existing file unless you run a migration.

Note that default property values aren’t applied to new objects or new properties on existing objects during migrations. We consider this to be a bug, and are tracking it as #1793.

Local migrations

Local migrations are defined by setting RLMRealmConfiguration.schemaVersion and RLMRealmConfiguration.migrationBlock. Your migration block provides all the logic for converting data models from previous schemas to the new schema. When creating a RLMRealm with this configuration, the migration block will be applied to update the RLMRealm to the given schema version if a migration is needed.

Suppose we want to migrate the Person model declared earlier. The minimal necessary migration block would be the following:

// Inside your [AppDelegate didFinishLaunchingWithOptions:]

RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
// Set the new schema version. This must be greater than the previously used
// version (if you've never set a schema version before, the version is 0).
config.schemaVersion = 1;

// Set the block which will be called automatically when opening a Realm with a
// schema version lower than the one set above
config.migrationBlock = ^(RLMMigration *migration, uint64_t 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
    }
};

// Tell Realm to use this new configuration object for the default Realm
[RLMRealmConfiguration setDefaultConfiguration:config];

// Now that we've told Realm how to handle the schema change, opening the file
// will automatically perform the migration
[RLMRealm defaultRealm];

At the very minimum we need to update the version with an empty block to indicate that the schema has been upgraded (automatically) by Realm.

Updating values

While this is the minimum acceptable migration, we probably want to use this block to populate any new properties (in this case fullName) with something meaningful. Within the migration block we can call [RLMMigration enumerateObjects:block:] to enumerate each RLMObject 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:]

RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.schemaVersion = 1;
config.migrationBlock = ^(RLMMigration *migration, uint64_t 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"]];
        }];
    }
};
[RLMRealmConfiguration setDefaultConfiguration:config];

Once the migration is successfully completed, the Realm and all of its objects can be accessed as usual by your app.

Renaming properties

Renaming the property on a class as part of a migration is more efficient than copying values and preserves relationships rather than duplicating them.

To rename a property during a migration, make sure that your new models have a property with the new name and don’t have a property with the old name.

If the new property has different nullability or indexing settings, those will be applied during the rename operation.

Here’s how you could rename Person’s yearsSinceBirth property to age:

// Inside your [AppDelegate didFinishLaunchingWithOptions:]

RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.schemaVersion = 1;
config.migrationBlock = ^(RLMMigration *migration, uint64_t oldSchemaVersion) {
    // We haven’t migrated anything yet, so oldSchemaVersion == 0
    if (oldSchemaVersion < 1) {
        // The renaming operation should be done outside of calls to `enumerateObjects:`.
        [migration renamePropertyForClass:Person.className oldName:@"yearsSinceBirth" newName:@"age"];
    }
};
[RLMRealmConfiguration setDefaultConfiguration:config];

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. It’s likely that JP has seen every new version of our app, and every schema upgrade in order: he downloaded a version of the app that took him from v0 to v1, and later another update that took him from v1 to v2. In contrast, it’s possible that Tim might download an update of the app that will 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 email 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 email 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 the email property on the if (oldSchemaVersion < 3) statement, guaranteeing that all Realms upgraded to version 3 will have a correct dataset.

Syncing migrations

When your Realm is synced with Realm Object Server, the migration process is a little different—and in many cases, simpler. Here’s what you need to know:

  • You don’t need to set the schema version (although you can).
  • Additive changes, such as adding a class or adding a field to a class, are applied automatically.
  • Removing a field from a schema doesn’t delete the field from the database, but instead instructs Realm to ignore that field. New objects will continue to be created with those properties, but they will be set to null. Non-nullable fields will be set to appropriate zero/empty values: 0 for numeric fields, an empty string for string properties, and so on.
  • You must not include a migration block.

Suppose your application has the Dog class:

@interface Dog : RLMObject
@property NSString *name;
@end

Now you need to add the Person class and give it an owner relationship to Dog. You don’t need to do anything other than adding the class and associated properties before syncing:

@interface Dog : RLMObject
@property NSString *name;
@property Person   *owner;
@end
RLM_ARRAY_TYPE(Dog)

@interface Person : RLMObject
@property NSString *name;
@property NSDate   *birthdate;
@end
RLM_ARRAY_TYPE(Person)

// Required properties of an Objective‑C reference
// type have to be declared in combination with:
@implementation Person
+ (NSArray *)requiredProperties {
    return @[@"name"];
}
@end

NSURL *syncServerURL = [NSURL URLWithString:@"http://localhost:9080/Dogs"];
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.syncConfiguration = [[RLMSyncConfiguration alloc] initWithUser:user realmURL:syncServerURL];

RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];

Since synced Realms don’t support migration blocks, destructive changes for a migration—changing a primary key, changing field types of existing fields (while keeping the same name), or changing a property from optional to required or vice-versa—need to be handled in a different way. Create a new synchronized Realm with the new schema, and copy data from the old Realm to the new Realm:

@interface Dog : RLMObject
@property NSString *name;
@property Person *owner;
@end
RLM_ARRAY_TYPE(Dog)

@interface Person : RLMObject
@property NSString *name;
@end
RLM_ARRAY_TYPE(Person)

@implementation Person
+ (NSArray *)requiredProperties {
    return @[@"name"];
}

@interface PersonV2 : RLMObject
@property NSString *name;
@end
RLM_ARRAY_TYPE(PersonV2)

@implementation PersonV2
+ (NSArray *)requiredProperties {
    return @[];
}

NSURL *syncServerURL = [NSURL URLWithString: @"realm://localhost:9080/Dogs"];
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.syncConfiguration = [[RLMSyncConfiguration alloc] initWithUser:user realmURL:syncServerURL];
// Limit to initial object type
config.objectClasses = @[Dog.class, Person.class];

RLMRealm *initialRealm = [RLMRealm realmWithConfiguration:config error:nil];

syncServerURL = [NSURL URLWithString: @"realm://localhost:9080/DogsV2"];
config = [RLMRealmConfiguration defaultConfiguration];
config.syncConfiguration = [[RLMSyncConfiguration alloc] initWithUser:user realmURL:syncServerURL];
// Limit to new object types
config.objectClasses = @[Dog.class, PersonV2.class];

RLMRealm *newRealm = [RLMRealm realmWithConfiguration:config error:nil];

Custom migrations can also be applied to synced Realms by writing a notification handler on the client side to make the changes, or as a JavaScript function on the server using the Node.js SDK (if you are using an edition of Object Server that supports it). However, if the migration makes a destructive change, the Realm will stop syncing with ROS, producing a Bad changeset received error.

Notifications

It is possible to register a listener to receive notifications for changes on a Realm or its entities. Realm notifications are sent when the Realm as a whole is changed; collection notifications are sent when individual objects are changed, added, or removed.

Notifications are delivered as long as a reference is held to the returned notification token. You should keep a strong reference to this token on the class registering for updates, as notifications are automatically unregistered when the notification token is deallocated.

Notifications are always delivered on the thread that they were originally registered on. That thread must have a currently running run loop. If you wish to register notifications on a thread other than the main thread, you are responsible for configuring and starting a run loop on that thread if one doesn’t already exist.

Notification handlers are asynchronously called after each relevant write transaction is committed, no matter which thread or process the write transaction took place on.

If a Realm is advanced to the latest version as part of starting a write transaction, notification handlers might be called synchronously. This will happen if, when the Realm was advanced to the latest version, Realm entities being observed were modified or deleted in a way that would trigger notifications. Such notifications will run within the context of the current write transaction, meaning attempting to begin a write transaction within the notification handler will cause Realm to throw an exception. If your app is architected in such a way that this scenario could occur, you can use -[RLMRealm isInWriteTransaction] to determine whether you are already inside a write transaction or not.

Since notifications are delivered using a run loop, the delivery of notifications might be delayed by other activity on the run loop. When notifications can’t be delivered immediately, changes from multiple write transactions may be coalesced into a single notification.

Realm notifications

A notification handler can be registered on an entire Realm. Every time a write transaction involving that Realm is committed, no matter which thread or process the write transaction took place on, the notification handler will be fired:

// Observe Realm Notifications
token = [realm addNotificationBlock:^(NSString *notification, RLMRealm * realm) {
    [myViewController updateUI];
}];

// later
[token invalidate];

Collection notifications

Collection notifications don’t receive the whole Realm, but instead receive fine-grained descriptions of changes. These consist of the indices of objects that have been added, removed, or modified since the last notification. Collection notifications are delivered asynchronously, first with the initial results and then again after each write transaction which changes any of the objects in the collection (or adds new ones).

These changes can be accessed via the RLMCollectionChange parameter that is passed to the notification block. This object holds information about the indices affected by deletions, insertions and modifications.

The first two, deletions and insertions, record the indices whenever objects start and stop being part of the collection. This takes into account when you add objects to the Realm or delete them from the Realm. For RLMResults this also applies when you filter for specific values and the object was changed so that it is now matching the query or not matching anymore. For collections based either on RLMArray or RLMLinkingObjects, including derived RLMResults, this applies in addition when objects are added or removed from the relationship.

You’re notified about modifications whenever a property of an object in the collection has changed. This also happens for changes on to-one and to-many relationships, although notifications won’t take inverse relationships into account.

@interface Dog : RLMObject
@property NSString *name;
@property NSData   *picture;
@property NSInteger age;
@end

@implementation Dog
@end

RLM_ARRAY_TYPE(Dog)
@interface Person : RLMObject
@property NSString             *name;
@property RLMArray<Dog *><Dog> *dogs;
@end

@implementation Person
@end

Let’s assume you’re observing a list of dog owners as given by the model code above. You will be notified about modifications for a matched Person object when:

  • You modify the Person’s name property.
  • You add or remove a Dog to the Person’s dogs property.
  • You modify the age property of a Dog belonging to that Person.

This makes it possible to discretely control the animations and visual updates made to the content inside your UI, instead of arbitrarily reloading everything each time a notification occurs.

- (void)viewDidLoad {
    [super viewDidLoad];

    // Observe RLMResults Notifications
    __weak typeof(self) weakSelf = self;
    self.notificationToken = [[Person objectsWhere:@"age > 5"] 
      addNotificationBlock:^(RLMResults<Person *> *results, RLMCollectionChange *changes, NSError *error) {
        
        if (error) {
            NSLog(@"Failed to open Realm on background worker: %@", error);
            return;
        }

        UITableView *tableView = weakSelf.tableView;
        // Initial run of the query will pass nil for the change information
        if (!changes) {
            [tableView reloadData];
            return;
        }

        // Query results have changed, so apply them to the UITableView
        [tableView beginUpdates];
        [tableView deleteRowsAtIndexPaths:[changes deletionsInSection:0]
                         withRowAnimation:UITableViewRowAnimationAutomatic];
        [tableView insertRowsAtIndexPaths:[changes insertionsInSection:0]
                         withRowAnimation:UITableViewRowAnimationAutomatic];
        [tableView reloadRowsAtIndexPaths:[changes modificationsInSection:0]
                         withRowAnimation:UITableViewRowAnimationAutomatic];
        [tableView endUpdates];
    }];
}

- (void)dealloc {
    [self.notificationToken invalidate];
}

Object notifications

Realm supports object-level notifications. You may register a notification on a particular Realm object in order to be notified if the object is deleted, or whenever any of the managed properties on the object have their values modified. (This also applies to managed properties that have their values set to their existing value.)

Only objects managed by a Realm may have notification handlers registered on them.

For write transactions performed on different threads or in different processes, the block will be called when the Realm that manages the object is (auto)refreshed to a version including the changes, while for local write transactions it will be called at some point in the future after the write transaction is committed.

The notification handler takes three arguments. The first argument, deleted, is a BOOL indicating whether the object was deleted. If this is YES, the other arguments will be nil and the block will never be called again.

The second argument, changes, is an NSArray of RLMPropertyChange objects. Each of these objects contains the name of a property that was changed (as a string), the previous value, and the current value.

The third argument is an NSError. If an error involving the object occurs, the NSError will contain information about what happened, changes will be nil, deleted will be NO, and the block will never be called again.

@interface RLMStepCounter : RLMObject
@property NSInteger steps;
@end

@implementation RLMStepCounter
@end

RLMStepCounter *counter = [[RLMStepCounter alloc] init];
counter.steps = 0;
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
[realm addObject:counter];
[realm commitWriteTransaction];

__block RLMNotificationToken *token = [counter addNotificationBlock:^(BOOL deleted,
                                                                      NSArray<RLMPropertyChange *> *changes,
                                                                      NSError *error) {
    if (deleted) {
        NSLog(@"The object was deleted.");
    } else if (error) {
        NSLog(@"An error occurred: %@", error);
    } else {
        for (RLMPropertyChange *property in changes) {
            if ([property.name isEqualToString:@"steps"] && [property.value integerValue] > 1000) {
                NSLog(@"Congratulations, you've exceeded 1000 steps.");
                [token invalidate];
                token = nil;
            }
        }

    }
}];

Interface-driven writes

Notifications in Realm are always delivered asynchronously so they never block the main UI thread, causing your app to stutter. However, there are situations when changes need to be done synchronously, on the main thread, and reflected in the UI instantly. We refer to these transactions as interface-driven writes.

For example, say a user adds an item to a table view. The UI should ideally animate this operation and start this process as soon as the user initiates the action.

However, when the Realm change notification for this insertion is delivered a little later, it will indicate that an object was added to the collection backing the table view and we will once again attempt to insert a new row in the UI. This double insertion leads to inconsistent state between the UI and the backing data, which in turn will crash your app!

When performing an interface-driven write, pass the notification tokens of the notification blocks that shouldn’t react to a change for a second time to -[RLMRealm commitWriteTransactionWithoutNotifying:error:].

This feature is especially useful when using fine-grained collection notifications with a synchronized Realm, because many of the workarounds to previously account for interface-driven writes rely on controlling the full state of when the app can perform changes. With synchronized Realms, changes are applied whenever they’re synchronized, which can happen at any point in the app’s lifetime.

// Observe RLMResults Notifications
__weak typeof(self) weakSelf = self;
self.notificationToken = [self.collection addNotificationBlock:^(RLMResults<Item *> *results, RLMCollectionChange *changes, NSError *error) {
    if (error) {
        NSLog(@"Failed to open Realm on background worker: %@", error);
        return;
    }

    UITableView *tableView = weakSelf.tableView;
    // Initial run of the query will pass nil for the change information
    if (!changes) {
        [tableView reloadData];
        return;
    }

    // Query results have changed, so apply them to the UITableView
    [tableView beginUpdates];
    [tableView deleteRowsAtIndexPaths:[changes deletionsInSection:0]
                     withRowAnimation:UITableViewRowAnimationAutomatic];
    [tableView insertRowsAtIndexPaths:[changes insertionsInSection:0]
                     withRowAnimation:UITableViewRowAnimationAutomatic];
    [tableView reloadRowsAtIndexPaths:[changes modificationsInSection:0]
                     withRowAnimation:UITableViewRowAnimationAutomatic];
    [tableView endUpdates];
}];

- (void)insertItem {
    // Perform an interface-driven write on the main thread:
    [self.collection.realm beginWriteTransaction];
    [self.collection insertObject:[Item new] atIndex:0];
    // And mirror it instantly in the UI
    [tableView insertRowsAtIndexPaths:[NSIndexPath indexPathForRow:0 inSection:0]
                     withRowAnimation:UITableViewRowAnimationAutomatic];
    // Making sure the change notification doesn't apply the change a second time
    [self.collection.realm commitWriteTransactionWithoutNotifying:@[token]];
}

Key-value observation

Realm objects are key-value observing compliant for most properties. Almost all managed (non-ignored) properties on RLMObject subclasses are KVO-compliant, along with the invalidated property on RLMObject and RLMArray. (RLMLinkingObjects properties can’t be observed using KVO.)

Observing properties of unmanaged instances of RLMObject subclasses works just like with any other NSObject subclass, but note that you cannot add an object to a Realm (with [realm addObject:obj] or other similar methods) while it has any registered observers.

Observing properties of managed objects (those previously added to a Realm) works a little differently. With managed objects, there are three times when the value of a property may change: when you directly assign to it; when you call [realm refresh] or the Realm is automatically refreshed after a write transaction is committed on a different thread; and when you call [realm beginWriteTransaction] after changes on a different thread which have not been picked up by a refresh on the current thread.

In the latter two cases, all of the changes made in the write transaction(s) on another thread will be applied at once, and KVO notifications will all be sent at once. Any intermediate steps are discarded, so if in the write transaction you incremented a property from one to ten, on the main thread you’ll get a single notification of a change directly from one to ten. Because properties can change in value when not in a write transaction or even as part of beginning a write transaction, trying to modify managed Realm objects from within -observeValueForKeyPath:ofObject:change:context: is not recommended.

Unlike NSMutableArray properties, observing changes made to RLMArray properties does not require using -mutableArrayValueForKey:, although that is supported for compatibility with code not written with Realm in mind. Instead, you can simply call the modification methods on RLMArray directly, and anyone observing the property it is stored in will be notified.

In our example apps you can find a short example of using Realm with ReactiveCocoa from Objective‑C, and ReactKit from Swift.

Encryption

Please take note of the Export Compliance section of our LICENSE, as it places restrictions against the usage of Realm if you are located in countries with an export restriction or embargo from the United States.

Realm supports encrypting the database file on disk with AES-256+SHA2 by supplying a 64-byte encryption key when creating a Realm.

// Generate a random encryption key
NSMutableData *key = [NSMutableData dataWithLength:64];
(void)SecRandomCopyBytes(kSecRandomDefault, key.length, (uint8_t *)key.mutableBytes);

// Open the encrypted Realm file
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.encryptionKey = key;
NSError *error = nil;
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:&error];
if (!realm) {
    // If the encryption key is wrong, `error` will say that it's an invalid database
    NSLog(@"Error opening realm: %@", error);
}

// Use the Realm as normal
RLMResults<Dog *> *dogs = [Dog objectsInRealm:realm where:@"name contains 'Fido'"];

This makes it so that all of the data stored on disk is transparently encrypted and decrypted with AES-256 as needed, and verified with a SHA-2 HMAC. The same encryption key must be supplied every time you obtain a Realm instance.

See our encryption sample app for an end-to-end app that generates an encryption key, stores it securely in the keychain, and uses it to encrypt a Realm.

There is a small performance hit (typically less than 10% slower) when using encrypted Realms.

Users

The central object in the Realm Object Server is the Realm user (RLMSyncUser) associated with a synchronized Realm. A RLMSyncUser can be authenticated to a shared Realm via a username/password scheme, or through a number of third-party authentication methods.

Creating and logging in a user requires two things:

  • A URL of a Realm Object Server to connect to.
  • Credentials for an authentication mechanism that describes the user as appropriate for that mechanism (i.e., username/password, access key, etc).

Server URL

The authentication server URL is simply a NSURL representing the location of the Realm Object Server.

NSURL *serverURL = [NSURL URLWithString:@"http://my.realmServer.com:9080"];

For more details about the server URL, please refer to our authentication documentation.

Authentication

Authentication is used to establish the identity of users and log them in. Refer to our authentication documentation for a list of authentication providers supported by the Realm Mobile Platform.

The credential information for a given user is represented by a RLMSyncCredentials value, which can be created in one of several ways:

  • Providing a valid username/password combination
  • Providing a token obtained from a supported third-party authentication service
  • Providing a token and a custom authentication provider (see our documentation on custom authentication)

The username and password authentication is entirely managed by the Realm Object Server, giving you full control over your application’s user management. For other authentication methods, your application is responsible for logging into the external service and obtaining the authentication token.

Here are some examples of setting credentials with various providers.

Username/password

RLMSyncCredentials *usernameCredentials = [RLMSyncCredentials credentialsWithUsername:@"username"
                                                                             password:@"password"
                                                                             register:NO];

Note that the factory method takes a register boolean argument that indicates whether a new user should be registered or an existing user should be logged in. An error will be thrown if your application tries to register a new user with the username of an existing user, or tries to log in a user that does not exist.

Google

RLMSyncCredentials *googleCredentials = [RLMSyncCredentials credentialsWithGoogleToken:@"Google token"];

Facebook

RLMSyncCredentials *facebookCredentials = [RLMSyncCredentials credentialsWithFacebookToken:@"Facebook token"];

Apple CloudKit

RLMSyncCredentials *cloudKitCredentials = [RLMSyncCredentials credentialsWithCloudKitToken:@"CloudKit token"];

Custom authentication

The Realm Object Server supports the ability to use external authentication providers. This allow users to authenticate users against legacy databases or APIs, or integrate with providers that are not supported out-of-the-box by the Realm Object Server. For information on how to write a custom authentication provider, see that section of the Object Server manual.

RLMSyncCredentials *credentials = [[RLMSyncCredentials alloc] initWithCustomToken:@"custom token" provider:@"myauth" userInfo:nil];

Additional information may be passed to the custom credentials constructor as a third parameter. Please see the API reference for more information.

Logging in

To create a user, call +[RLMSyncUser logIn]. This factory method will initialize the user, asynchronously log them in to the Realm Object Server, and then provide the user object for further use in a callback block if the login process is successful. If the login process fails for any reason, an error object will be passed into the callback instead.

[RLMSyncUser logInWithCredentials:usernameCredentials
                    authServerURL:serverURL
                     onCompletion:^(RLMSyncUser *user, NSError *error) {
    if (user) {
        // can now open a synchronized RLMRealm with this user
    } else if (error) {
        // handle error
    }
}];

The Realm Mobile Platform allows an application to support multiple users at the same time.

For example, consider an email client app that supports connecting to multiple email accounts. Multiple users (one for each email account) can be authenticated at any given time, with all active users being available via +[RLMSyncUser all].

Working with users

The currentUser method will retrieve the current user, the last user who logged in and whose credentials have not expired. The method returns nil if no current user exists. An exception will be thrown if more than one logged-in user exists.

RLMSyncUser *user = [RLMSyncUser currentUser];

If there are multiple users logged in, you can get a dictionary of user objects corresponding to them with allUsers.

NSDictionary<NSString *, RLMSyncUser *> *allUsers = [RLMSyncUser allUsers];

If no users are logged in, the dictionary returned will be empty.

Users who authenticate using the built-in Realm Object Server username/password credential type may change their own passwords by calling the -[RLMSyncUser changePassword:completion:] API.

This API makes an asynchronous call to the server. The completion block will be called once a response is received, and will be passed in an error if the operation failed, or nil if the operation succeeded.

NSString *newPassword = @"swordfish";
[user changePassword:newPassword
          completion:^(NSError *error) {
    if (error) {
        // Something went wrong
    }
    // Otherwise, the password was successfully changed.
}];

Logging out

To log a user out of their account, call -[RLMSyncUser logOut]. Any pending local changes will continue to be uploaded until the Realm Object Server has been fully synchronized. Then, all of their local synced Realms will be deleted from their device on next app launch.

Admin users

Admin users are Realm Object Server users that have management-level access to all Realms on a ROS instance. Whether or not a user is an admin user is reflected by the RLMSyncUser.isAdmin property, which reflects the user’s state at the time of its last successful login.

[RLMSyncUser logInWithCredentials:usernameCredentials
                    authServerURL:serverURL
                     onCompletion:^(RLMSyncUser *user, NSError *error) {
    if (user) {
        // can now open a synchronized RLMRealm with this user
        // true if the user is an administrator on the ROS instance
        NSLog(@"User is admin: %@", @(user.isAdmin));
    } else if (error) {
        // handle error
    }
}];

Access Control

The Realm Mobile Platform provides flexible access control mechanisms to specify which users are allowed to work with specific synchronized Realms. This can be used, for example, to create collaborative apps where multiple users write to the same Realm. It can also be used to share data in a publisher/subscriber scenario where a single writing user shares data with many users with read permissions.

There are three levels that control access to a given Realm:

  • mayRead indicates that the user is allowed to read from the Realm,
  • mayWrite indicates that the user is allowed to write to the Realm,
  • mayManage indicates that the user is allowed to change the permissions for the Realm.

While these are implemented as boolean flags, in practice, each level should include the previous level: mayWrite should be set with mayRead, and mayManage should be set with all three. (Read-only Realms must be opened asynchronously.)

Unless permissions are explicitly modified, only the owner of a Realm can access it. The only exception is administrator users: they are always granted all permissions to all Realms on the server.

Write-only permissions (i.e., mayWrite set without mayRead) are not currently supported.

Please refer to the general Realm Object Server documentation on Access Control to learn more about access control.

Reading permissions

To get all the Realms a user has access to, along with the level of access for each Realm, use the -[RLMSyncUser retrievePermissionsWithCallback:] method.

[[RLMSyncUser currentUser] retrievePermissionsWithCallback:^(RLMResults<RLMSyncPermission *> *permissions, NSError *error) {
    if (error) {
        // handle error
        return;
    }
    // success! access permissions
}];

Modifying permissions

Modifying the access control settings for a Realm file is performed through one of two means: applying/revoking permission values and offer/response objects.

Granting permissions

Permission values can be applied (i.e. granted) to other users in order to directly increase or decrease their access to a Realm.

RLMSyncPermission *permission = [[RLMSyncPermission alloc] initWithRealmPath:realmPath                 // The remote Realm path on which to apply the changes
                                                                    identity:anotherUserID             // The user ID for which these permission changes should be applied
                                                                 accessLevel:RLMSyncAccessLevelWrite]; // The access level to be granted
[user applyPermission:permission callback:^(NSError *error) {
    if (error) {
        // handle error
        return;
    }
    // permission was successfully applied
}];

To apply the permission changes for all Realms managed by the user, specify a realmPath value of *. To apply the permission changes for all users authorized with the Object Server, specify a userID value of *.

In addition to granting permissions based on the Realm Object Server identity of the user, it is also possible to grant permissions based on their Realm Object Server username:

RLMSyncPermission *permission = [[RLMSyncPermission alloc] initWithRealmPath:realmPath
                                                                    username:@"alice@realm.example.org"
                                                                 accessLevel:RLMSyncAccessLevelWrite];
[user applyPermission:permission callback:^(NSError *error) {
    // ...
}];

Revoking permissions

Revoking permissions can either be done by granting a permission value with an access level of RLMSyncAccessLevelNone or by passing a permission value with any level to -[RLMSyncUser revokePermission:callback:].

Offering permissions

Permission offers can be used to share Realms between users. You need not write any server code; permission offers are created and accepted entirely through the client APIs.

If you want to share access to a Realm that you own or manage, create a permission offer. To do so, call the -[RLMSyncUser createOfferForRealmAtURL:accessLevel:expiration:callback:] method. This method will asynchronously make a permission offer; the callback will be called with a string token representing the permission offer once the operation completes successfully.

Permission offers specify the URL of the Realm to offer access to, what level of access to grant to the recepient, and a date after which the permission offer expires and can no longer be redeemed. (Users who have already accepted the offer do not lose access to the Realm.) If this date is specified as nil, the offer will never expire.

NSURL *realmURL = [NSURL URLWithString:@"realm://realm.example.org/~/recipes"];

// Offer read-write access to `offeringUser`'s personal instance of the `recipes` Realm.
[offeringUser createOfferForRealmAtURL:realmURL
                           accessLevel:RLMSyncAccessLevelWrite
                            expiration:nil
                              callback:^(NSString *token, NSError *error) {
    if (error) {
        NSLog(@"Not able to create a permission offer! Error was: %@", error);
        return;
    }
    // (`token` can now be passed out of the block and given to another user...)
}];

This string token can then be passed to a different user via any appropriate channel (such as e-mail) and accepted using the -[RLMSyncUser acceptOfferForToken:callback:] method. This method also runs asynchronously; the callback will be called with the URL of the Realm which was represented by the offer. This URL can then be used to open Realms as the recepient.

NSString *token;
// (get token...)

[receivingUser acceptOfferForToken:token callback:^(NSURL *realmURL, NSError *error) {
    if (error) {
        NSLog(@"Not able to accept a permission offer! Error was: %@", error);
        return;    
    }
    // We can now use `realmURL` to open the Realm.
    // (Remember that if we only have read permissions, we must use the `asyncOpen` API.)
    RLMSyncConfiguration *syncConfig = [[RLMSyncConfiguration alloc] initWithUser:receivingUser realmURL:realmURL];
    RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
    config.syncConfiguration = syncConfig;
    RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:&error];
    // ...
}];

Note that a user’s device must be able to communicate with a Realm Object Server in order to create and accept permission offers.

Permissions granted by permission offers are additive: if a user has write access to a certain Realm and is offered (and accepts) read access to that same Realm, they do not lose their existing write access.

Working with synced Realms

Sync sessions

A synced Realm’s connection to the Realm Object Server is represented by a RLMSyncSession object. Session objects representing Realms opened by a specific user can be retrieved from that user’s RLMSyncUser object using the +[RLMSyncUser allSessions] or -[RLMSyncUser sessionForURL:] APIs.

Basic operations

The state of the underlying session can be retrieved using the state property. This can be used to check whether the session is active, not connected to the server, or in an error state. If the session is valid, the configuration property will contain a RLMSyncConfiguration value that can be used to open another instance of the same Realm (for example, on a different thread).

Progress notifications

Session objects allow your app to monitor the status of a session’s uploads to and downloads from the Realm Object Server by registering progress notification blocks on a session object.

Progress notification blocks will be invoked periodically by the synchronization subsystem on the runloop of the thread in which they were originally registered. If no runloop exists, one will be created. (Practically speaking, this means you can register these blocks on background queues in GCD and they will work fine.) As many blocks as needed can be registered on a session object simultaneously. Blocks can either be configured to report upload progress or download progress.

Each time a block is called, it will receive the current number of bytes already transferred, as well as the total number of transferrable bytes (defined as the number of bytes already transferred plus the number of bytes pending transfer).

When a block is registered, the registration method returns a token object. The -invalidate method can be called on the token to stop notifications for that particular block. If the block has already been deregistered, calling the stop method does nothing. Note that the registration method might return a nil token if the notification block will never run again (for example, because the session was already in a fatal error state, or there is no further progress to report).

There are two types of blocks. Blocks can be configured to report indefinitely. These blocks will remain active unless explicitly stopped by the user and will always report the most up-to-date number of transferrable bytes. This type of block could be used to control a network indicator UI that, for example, changes color or appears only when uploads or downloads are actively taking place.

RLMSyncSession *session = [[RLMSyncUser currentUser] sessionForURL:realmURL];
void(^workBlock)(NSUInteger, NSUInteger) = ^(NSUInteger downloaded, NSUInteger downloadable) {
    if ((double)downloaded/(double)downloadable >= 1) {
        [viewController hideActivityIndicator];
    } else {
        [viewController showActivityIndicator];
    }
};
RLMProgressNotificationToken *token;
token = [session addProgressNotificationForDirection:RLMSyncProgressDirectionDownload
                                                mode:RLMSyncProgressModeReportIndefinitely
                                               block:workBlock];

// Much later...
[token invalidate];

Blocks can also be configured to report progress for currently outstanding work. These blocks capture the number of transferrable bytes at the moment they are registered and always report progress relative to that value. Once the number of transferred bytes reaches or exceeds that initial value, the block will automatically unregister itself. This type of block could, for example, be used to control a progress bar that tracks the progress of an initial download of a synced Realm when a user signs in, letting them know how long it is before their local copy is up-to-date.

RLMSyncSession *session = [[RLMSyncUser currentUser] sessionForURL:realmURL];
RLMProgressNotificationToken *token;
void(^workBlock)(NSUInteger, NSUInteger) = ^(NSUInteger uploaded, NSUInteger uploadable) {
    double progress = ((double)uploaded/(double)uploadable);
    progress = progress > 1 ? 1 : progress;
    [viewController updateProgressBarWithFraction:progress];
    if (progress == 1) {
        [viewController hideProgressBar];
        [token invalidate];
    }
};
token = [session addProgressNotificationForDirection:RLMSyncProgressDirectionUpload
                                                mode:RLMSyncProgressModeForCurrentlyOutstandingWork
                                               block:workBlock];

Logging

The synchronization subsystem supports a number of logging levels, useful while developing an app. These can be selected by setting the logLevel property on the RLMSyncManager singleton to the desired verbosity:

[[RLMSyncManager sharedManager] setLogLevel:RLMSyncLogLevelOff];

The logging level must be set before any synced Realms are opened. Changing it after the first synced Realm is opened will have no effect.

The logging levels are described in the Realm Object Server configuration documentation.

Error reporting

Certain sync-related APIs perform asynchronous operations that may fail. These APIs take completion blocks which accept an error parameter; if the error parameter is passed in the operation failed. The error can be checked for more detail.

It is strongly recommended to also set an error handler on the RLMSyncManager singleton. Errors involving the global synchronization subsystem or specific sessions (which represent Realms opened for synchronization with the server) will be reported through this error handler. When errors occur, the error handler will be called with an object representing the error, as well as a RLMSyncSession object representing the session the error occurred on (if applicable).

[[RLMSyncManager sharedManager] setErrorHandler:^(NSError *error, RLMSyncSession *session) {
    // handle error
}];

Realm Mobile Platform errors are represented by NSError objects whose domain is RLMSyncErrorDomain. Please consult the definitions of RLMSyncError and RLMSyncAuthError for error codes and what they mean.

Client reset

If a Realm Object Server crashes and must be restored from a backup, there is a possibility that an app might need to carry out a client reset on a given synced Realm. This will happen if the local version of the Realm in question is greater than the version of that same Realm on the server (for example, if the application made changes after the Realm Object Server was backed up, but those changes weren’t synced back to the server before it crashed and had to be restored).

The client reset procedure is as follows: a backup copy of the local Realm file is made, and then the Realm files are deleted. The next time the app connects to the Realm Object Server and opens that Realm, a fresh copy will be downloaded. Changes that were made after the Realm Object Server was backed up but weren’t synced back to the server will be preserved in the backup copy of the Realm, but will not be present when the Realm is re-downloaded.

The need for a client reset is indicated by an error sent to the RLMSyncManager error handler. This error will be denoted by the code RLMSyncErrorClientResetError.

The error object will also contain two values: the location of the backup copy of the Realm file once the client reset process is carried out, and a token object which can be passed into the +[RLMSyncSession immediatelyHandleError:] method to manually initiate the client reset process.

If the client reset process is manually initiated, all instances of the Realm in question must first be invalidated and destroyed. Note that a RLMRealm might not be fully invalidated, even if all references to it are nil’ed out, until the autorelease pool containing it is drained. However, doing so allows the Realm to be immediately re-opened after the client reset process is complete, allowing syncing to resume.

If the client reset process is not manually initiated, it will instead automatically take place after the next time the app is launched, upon first accessing the RLMSyncManager singleton. It is the app’s responsibility to persist the location of the backup copy if needed, so that the backup copy can be found later.

A Realm which needs to be reset can still be written to and read from, but no subsequent changes will be synced to the server until the client reset is complete and the Realm is re-downloaded. It is extremely important that your application listen for client reset errors and, at the very least, make provisions to save user data created or modified after a client reset is triggered so it can later be written back to the re-downloaded copy of the Realm.

The backup copy file path can be obtained by calling -[NSError rlmSync_clientResetBackedUpRealmPath] upon the NSError object. It can also be extracted directly from the userInfo dictionary using the kRLMSyncPathOfRealmBackupCopyKey key.

The reset initiation token can be obtained by calling -[NSError rlmSync_errorActionToken] upon the NSError object. It can also be extracted directly from the userInfo dictionary using the kRLMSyncErrorActionTokenKey key.

The following example shows how the client reset APIs might be used to carry out a client reset:

[[RLMSyncManager sharedManager] setErrorHandler:^(NSError *error, RLMSyncSession *session) {
    if (error.code == RLMSyncErrorClientResetError) {
        [realmManager closeRealmSafely];
        [realmManager saveBackupRealmPath:[error rlmSync_clientResetBackedUpRealmPath]];
        [RLMSyncSession immediatelyHandleError:[error rlmSync_errorActionToken]];
        return;
    }
    // Handle other errors...
}];

For more information about how the Realm Object Server handles client reset, please refer to our server documentation.

Permission denied errors

If a user attempts to perform operations on a Realm for which they do not have appropriate permissions, a permission denied error will be reported. Such an error may occur, for example, if a user attempts to write to a Realm for which they only have read permissions or opens a Realm for which they have no permissions at all.

It is important to note that if a user has only read access to a particular synchronized Realm they must open that Realm asynchronously using the +[RLMRealm asyncOpenWithConfiguration:callbackQueue:callback:] API. Failure to do so will lead to a permission denied error being reported.

A permission denied error will be denoted by the code RLMSyncErrorPermissionDeniedError . The error object will contain an opaque token which can be used to delete the offending Realm file. The token can be obtained by calling -[NSError rlmSync_errorActionToken] upon the NSError object. It can also be extracted directly from the userInfo dictionary using the kRLMSyncErrorActionTokenKey key.

In all cases, a permission denied error indicates that the local copy of the Realm on the user’s device has diverged in a way that cannot be reconciled with the server. The file will be automatically deleted the next time the application is launched. The token included with the error can also be passed into the +[RLMSyncSession immediatelyHandleError:] method in order to immediately delete the Realm files.

If the token is used to immediately delete the Realm files, all instances of the Realm in question must first be invalidated and destroyed. Note that a RLMRealm might not be fully invalidated, even if all references to it are nil’ed out, until the autorelease pool containing it is drained. However, doing so allows the Realm to be immediately re-opened in the correct way or after the correct permissions have been set, allowing syncing to resume.

Query-based synchronization

Query-based synchronization is a feature that allows a synchronized Realm to be opened in a such a way that it does not download all objects stored within the remote Realm on the server. Instead, a partially synced Realm allows you to specify what subset of objects you want synchronized to the local copy using queries.

Query-based synchronization support is currently a tech preview, and APIs related to this feature may change in future versions of Realm.

To open a Realm in Query-based synchronization mode, simply set the isPartial property on the RLMSyncConfiguration before using it to open the Realm.

A partially synced Realm will contain no objects upon initially being created and opened. To specify objects for the Realm to fetch, call the -[RLMRealm subscribeToObjects:where:callback:] method. Pass in the type of the objects you wish to fetch, a string containing a valid query, and a callback which will be invoked when the results are ready.

The callback will be called at most one time. If there was a problem fetching the results (for example, the query string was invalid), an error will be passed into the callback describing what went wrong. Otherwise, a RLMResults object will be passed in containing all the objects that matched the query. This results collection will update itself automatically as items on the remote Realm are added, changed, or removed. If you wish to observe further changes to its contents, add a collection notification to it.

You may modify objects within the results collection, and these changes will be synced up to the Realm Object Server. Note that conflicts may be resolved differently than if the writes had been made to a fully-synchronized copy of the Realm.

Migrating from Realm Object Server 1.* to 2.0

If you were using a prior version of Realm alongside a version of Realm Object Server prior to 2.0, and you upgrade your application to use Realm Object Server 2.0 or later, you must write code in your application to handle migrating your local copies of synchronized Realms. The Realm Object Server 2.0 file format is incompatible with earlier versions of the Realm Object Server, and all Realms that were synced from an older version of the Realm Object Server must be re-downloaded.

When a synced Realm requiring migration is opened, the Realm file will be copied to a backup location and then deleted so that it can be re-downloaded from the Realm Object Server. An error will then be passed back.

In order to handle this migration, use +[RLMRealm realmWithConfiguration:error:] to open the Realm and check the error that is returned. A migration error will be denoted by the code RLMErrorIncompatibleSyncedFile. The error’s userInfo dictionary contains a RLMRealmConfiguration under the RLMBackupRealmConfigurationErrorKey key that allows the backed up copy of the Realm to be opened. You may then write code to check the backed up copy of the Realm for any relevant data that was not synced to the server and manually write it back into the newly downloaded copy of the Realm.

Note that any Realm opened by the backup configuration will be opened as an ‘offline’ Realm; no synchronization will take place.

NSError *error = nil;
RLMRealm *realm = [RLMRealm realmWithConfiguration:configuration error:&error];
if (!realm && error.code == RLMErrorIncompatibleSyncedFile) {
    RLMRealmConfiguration *backupConfiguration = error.userInfo[RLMBackupRealmConfigurationErrorKey];
    RLMRealm *backupRealm = [RLMRealm realmWithConfiguration:backupConfiguration error:&error];
    // (...)
}
// Once we've opened the backup Realm, we can now try re-opening the Realm.
realm = [RLMRealm realmWithConfiguration:configuration error:&error];

Converting local Realms to synced Realms

Automatic conversions from local (non-synced) Realms to synced Realms are currently not supported. We plan to add support for this use case in the future.

If you have a local Realm that you wish to convert into a synced Realm, you must open a new synced Realm and then manually copy the objects from the local Realm into the synced Realm.

Conflict resolution

Conflict resolution support is described within the Realm Object Server documentation.

Administrator APIs

A number of APIs exist that only administrator users (users with the isAdmin flag set) may call.

Change passwords

Administrators may change the password of any user by calling the -[RLMSyncUser changePassword:forUserID:completion:] API. Pass in the Realm Object Server identity of the user whose password should be changed.

Retrieve user information

Administrators may retrieve information about any user from the Realm Object Server by calling the -[RLMSyncUser retrieveInfoForUser:identityProvider:completion:] API.

The first argument is a string containing the identity of the user as issued to them by the identity provider service. For example, if the user was registered using the Realm Object Server’s username/password functionality, this string would be the username. It should not be confused with the internal identity issued to the user by the Realm Object Server. The second argument is the identity provider with which the user was registered.

Results are returned asynchronously through the completion block. The block will be passed in an error if the lookup failed (for example, if no such user exists or if the user is not a Realm Object Server administrator), or a RLMSyncUserInfo object containing information about the user if successful.

NSString *targetUserIdentity = @"bob@realm.example.org";
[adminUser retrieveInfoForUser:targetUserIdentity
              identityProvider:RLMIdentityProviderUsernamePassword
                    completion:^(RLMSyncUserInfo *userInfo, NSError *error) {
    if (error) {
        // An error occurred
    }
    // Examine userInfo for the user's information...
}];

Threading

Realm read transaction lifetimes are tied to the memory lifetime of RLMRealm instances. Avoid “pinning” old Realm transactions by using auto-refreshing Realms and wrapping all use of Realm APIs from background threads in explicit autorelease pools.

Refer to our Current Limitations for more details on this effect.

Within individual threads you can just treat everything as regular objects without worrying about concurrency or multithreading. There is no need for any locks or resource coordination to access them (even if they are simultaneously being modified on other threads) and it is only modifying operations that have to be wrapped in write transactions.

Realm makes concurrent usage easy by ensuring that each thread always has a consistent view of the Realm. You can have any number of threads working on the same Realms in parallel, and since they all have their own snapshots, they will never cause each other to see inconsistent state.

The only thing you have to be aware of is that you cannot have multiple threads sharing the same instances of Realm objects. If multiple threads need to access the same objects they will each need to get their own instances (otherwise changes happening on one thread could cause other threads to see incomplete or inconsistent data).

Seeing changes from other threads

On the main UI thread (or any thread with a runloop) objects will automatically update with changes from other threads between each iteration of the runloop. At any other time you will be working on the snapshot, so individual methods always see a consistent view and never have to worry about what happens on other threads.

When you initially open a Realm on a thread, its state will be based off the most recent successful write commit, and it will remain on that version until refreshed. Realms are automatically refreshed at the start of every runloop iteration, unless RLMRealm’s autorefresh property is set to NO. If a thread has no runloop (which is generally the case in a background thread), then -[RLMRealm refresh] must be called manually in order to advance the transaction to the most recent state.

Realms are also refreshed when write transactions are committed (-[RLMRealm commitWriteTransaction]).

Failing to refresh Realms on a regular basis could lead to some transaction versions becoming “pinned,” preventing Realm from reusing the disk space used by that version, leading to larger file sizes.

Passing instances across threads

Unmanaged instances of RLMObjects behave exactly as regular NSObject subclasses, and are safe to pass across threads.

Instances of RLMRealm, RLMResults, or RLMArray, or managed instances of RLMObject are thread-confined, meaning that they can only be used on the thread on which they were created, otherwise an exception is thrown*. This is one way Realm enforces transaction version isolation. Otherwise, it would be impossible to determine what should be done when an object is passed between threads at different transaction versions without a potentially extensive relationship graph.

Realm exposes a mechanism to safely pass thread-confined instances in three steps:

  1. Initialize a RLMThreadSafeReference with the thread-confined object.
  2. Pass that RLMThreadSafeReference to a destination thread or queue.
  3. Resolve this reference on the target Realm by calling -[RLMRealm resolveThreadSafeReference:]. Use the returned object as you normally would.
Person *person = [Person new];
person.name = @"Jane";
[realm transactionWithBlock:^{
    [realm addObject:person];
}];
RLMThreadSafeReference *personRef = [RLMThreadSafeReference
    referenceWithThreadConfined:person];

dispatch_async(queue, ^{
    @autoreleasepool {
        RLMRealm *realm = [RLMRealm realmWithConfiguration:realm.configuration
                                                     error:nil];
        Person *person = [realm resolveThreadSafeReference:personRef];
        if (!person) {
            return; // person was deleted
        }
        [realm transactionWithBlock:^{
            person.name = @"Jane Doe";
        }];
    }
});

A RLMThreadSafeReference object must be resolved at most once. Failing to resolve a RLMThreadSafeReference will result in the source version of the Realm being pinned until the reference is deallocated. For this reason, RLMThreadSafeReference should be short-lived.

Some properties and methods on these types can be accessed from any thread:

  • RLMRealm: all properties, class methods, and initializers.
  • RLMObject: isInvalidated, objectSchema, realm, class methods, and initializers.
  • RLMResults: objectClassName and realm.
  • RLMArray: isInvalidated, objectClassName, and realm.

Using a Realm across threads

To access the same Realm file from different threads, you must initialize a new Realm to get a different instance for every thread of your app. As long as you specify the same configuration, all Realm instances will map to the same file on disk.

Sharing Realm instances across threads is not supported. Realm instances accessing the same Realm file must also all use the same RLMRealmConfiguration.

Realm can be very efficient when writing large amounts of data by batching together multiple mutations 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 a Realm 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, ^{
    @autoreleasepool {
        // 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. Property order is ignored.
            for (NSInteger idx2 = 0; idx2 < 1000; idx2++) {
                [Person createInRealm:realm
                            withValue:@{@"name"      : randomString,
                                        @"birthdate" : randomDate}];
            }

          // Commit the write transaction
          // to make this data available to other threads
          [realm commitWriteTransaction];
        }
    }
});

JSON

Realm does not have direct support for JSON, but it’s possible to add RLMObjects from JSON using the output of [NSJSONSerialization JSONObjectWithData:options:error:] . The resulting KVC-compliant object can be used to add/update RLMObjects using the standard APIs for creating and updating objects.

// A Realm Object that represents a city
@interface City : RLMObject
@property NSString *name;
@property NSInteger cityId;
// other properties left out ...
@end
@implementation City
@end // None needed

NSData *data = [@"{\"name\": \"San Francisco\", \"cityId\": 123}" dataUsingEncoding: NSUTF8StringEncoding];
RLMRealm *realm = [RLMRealm defaultRealm];

// Insert from NSData containing JSON
[realm transactionWithBlock:^{
    id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
    [City createOrUpdateInRealm:realm withValue:json];
}];

If there are nested objects or arrays in the JSON, these will be mapped automatically to to-one and to-many relationships. See the nested objects section for more detail.

When inserting or updating JSON data in a Realm using this approach, be aware that Realm expects the JSON property names and types to exactly match the RLMObject properties. For example:

  • float properties should be initialized with float-backed NSNumbers.
  • NSDate and NSData properties cannot be automatically inferred from strings, but should be converted to the appropriate type before passing to [RLMObject createOrUpdateInRealm:withValue:] .
  • If a JSON null (i.e. NSNull) is supplied for a required property, an exception will be thrown.
  • If no property is supplied on insert for a required property, an exception will be thrown.
  • Realm will ignore any properties in the JSON not defined by the RLMObject.

If your JSON schema doesn’t align exactly with your Realm objects, we recommend you use a third party model mapping framework in order to transform your JSON. Objective‑C has a thriving set of actively maintained model mapping frameworks which work with Realm, some of which are listed in the realm-cocoa repository.

Testing and debugging

Configuring the default Realm

The easiest way to use and test Realm apps is to use the default Realm. To avoid overriding application data or leaking state between tests, you can simply set the default Realm to a new file for each test.

// A base class which each of your Realm-using tests should inherit from rather
// than directly from XCTestCase
@interface TestCaseBase : XCTestCase
@end

@implementation TestCaseBase
- (void)setUp {
    [super setUp];

    // Use an in-memory Realm identified by the name of the current test.
    // This ensures that each test can't accidentally access or modify the data
    // from other tests or the application itself, and because they're in-memory,
    // there's nothing that needs to be cleaned up.
    RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
    config.inMemoryIdentifier = self.name;
    [RLMRealmConfiguration setDefaultConfiguration:config];
}
@end

Injecting Realm instances

Another way to test Realm-related code is to have all the methods you’d like to test accept a RLMRealm instance as an argument, so that you can pass in different Realms when running the app and when testing it. For example, suppose your app has a method to GET a user profile from a JSON API and you’d like to test that the local profile is properly created:

// Application Code

@implementation ClassBeingTested
+ (void)updateUserFromServer {
    NSURL *url = [NSURL URLWithString:@"http://myapi.example.com/user"];
    [[[NSURLSession sharedSession] dataTaskWithURL:url
                                 completionHandler:^(NSData *data,
                                                     NSURLResponse *response,
                                                     NSError *error) {
        [self createOrUpdateUserInRealm:[RLMRealm defaultRealm] withData:data];
    }] resume];
}

+ (void)createOrUpdateUserInRealm:(RLMRealm *)realm withData:(NSData *)data {
    id object = [NSJSONSerialization JSONObjectWithData:data options:(NSJSONReadingOptions)nil error:nil];
    [realm transactionWithBlock:^{
        [User createOrUpdateInRealm:realm withValue:object];
    }];
}
@end

// Test Code

@implementation UnitTests
- (void)testThatUserIsUpdatedFromServer {
    RLMRealm *testRealm = [RLMRealm realmWithURL:kTestRealmURL];
    NSData *jsonData = [@"{\"email\": \"help@realm.io\"}"
                        dataUsingEncoding:NSUTF8StringEncoding];
    [ClassBeingTested createOrUpdateUserInRealm:testRealm withData:jsonData];
    User *expectedUser = [User new];
    expectedUser.email = @"help@realm.io";
    XCTAssertEqualObjects([User allObjectsInRealm:testRealm][0],
                          expectedUser,
                          @"User was not properly updated from server.");
}
@end

Debugging

Debugging your Realm apps is easy, with the ability to view your app’s data in the Realm Studio.

Our SDK includes an LLDB script which adds support for inspecting managed RLMObjects, RLMResults and RLMArrays objects in Xcode’s UI, rather than just displaying every property as nil or 0:

Screenshot of Xcode Debugging Console

N.B.: The script currently only supports Objective‑C. Swift support is in progress.

If you’re using Realm as a dynamic framework, you’ll need to make sure your unit test target can find Realm. You can do this by adding the parent path to Realm.framework to your unit test’s “Framework Search Paths”.

If your tests fail with an exception message "Object type 'YourObject' is not managed by the Realm", it’s likely because you’ve linked the Realm framework directly to your test target, which should not be done. Unlinking Realm from your test target should address that.

You should also make sure to only compile your model class files in your application or framework targets; never add them to your unit test targets. Otherwise, those classes will be duplicated when testing, which can lead to difficult to debug issues (see this issue for details).

You’ll need to make sure all the code you need to test is exposed to your unit test targets (use the public access modifier or @testable). See this Stack Overflow answer for details.

Current limitations

Here’s a list of our most commonly hit limitations.

Please refer to our GitHub issues for a more comprehensive list of known issues.

General

Realm aims to strike a balance between flexibility and performance. In order to accomplish this goal, realistic limits are imposed on various aspects of storing information in a Realm. For example:

  1. Class names are limited to a maximum of 57 UTF8 characters.
  2. Property names are limited to a maximum of 63 UTF8 characters.
  3. NSData and NSString properties cannot hold data exceeding 16MB in size. To store larger amounts of data, either break it up into 16MB chunks or store it directly on the file system, storing paths to these files in the Realm. An exception will be thrown at runtime if your app attempts to store more than 16MB in a single property.
  4. Any single Realm file cannot be larger than the amount of memory your application would be allowed to map in iOS — this changes per device, and depends on how fragmented the memory space is at that point in time (there is a radar open about this issue: rdar://17119975). If you need to store more data, you can map it over multiple Realm files.
  5. String sorting and case insensitive queries are only supported for character sets in ‘Latin Basic’, ‘Latin Supplement’, ‘Latin Extended A’, ‘Latin Extended B’ (UTF-8 range 0-591).

Threads

Although Realm files can be accessed by multiple threads concurrently, you cannot directly pass Realms, Realm objects, queries, and results between threads. If you need to pass Realm objects between threads, you can use the RLMThreadSafeReference API. Read more about Realm’s threading.

Models

Setters and getters: 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 overridden, and can call other setters/getters.

Auto-incrementing properties: Realm has no mechanism for thread-safe/process-safe auto-incrementing properties commonly used in other databases when generating primary keys. However, in most situations where a unique auto-generated value is desired, it isn’t necessary to have sequential, contiguous, integer IDs. A unique string primary key is typically sufficient. A common pattern is to set the default property value to [[NSUUID UUID] UUIDString] to generate unique string IDs.

Another common motivation for auto-incrementing properties is to preserve order of insertion. In some situations, this can be accomplished by appending objects to a RLMArray or by using a createdAt property with a default value of [NSDate date].

-[NSPredicate evaluateWithObject:] rejects Realm collections as being non-collection objects: Due to some overly-constrained checks in NSPredicate’s internal implementation, some of NSPredicate’s APIs are incompatible with Realm’s collection types. For example, -[NSPredicate evaluateWithObject:] will throw an exception when a subquery predicate attempts to iterate over a Realm collection. Apple is aware of this issue (rdar://31252694).

If you need to work around this in your application, you can integrate the patch from PR #4770, invoking RLMWorkaroundRadar31252694() just once prior to performing any predicate evaluations.

File size

Realm read transaction lifetimes are tied to the memory lifetime of RLMRealm instances. Avoid “pinning” old Realm transactions by using auto-refreshing Realms and wrapping all use of Realm APIs from background threads in explicit autorelease pools.

You should expect a Realm database to take less space on disk than an equivalent SQLite database. If your Realm file is much larger than you expect, it may be because you have a RLMRealm that is referring to an older version of the data in the database.

In order to give you a consistent view of your data, Realm only updates the active version accessed at the start of a run loop iteration. This means that if you read some data from the Realm and then block the thread on a long-running operation while writing to the Realm on other threads, the version is never updated and Realm has to hold on to intermediate versions of the data which you may not actually need, resulting in the file size growing with each write. The extra space will eventually be reused by future writes, or may be compacted—for example, by setting shouldCompactOnLaunch or calling writeCopyToPath:error: . To avoid this issue, you may call invalidate to tell Realm that you no longer need any of the objects that you’ve read from the Realm so far, which frees us from tracking intermediate versions of those objects. The Realm will update to the latest version the next time it is accessed.

You may also see this problem when accessing Realm using Grand Central Dispatch. This can happen when a Realm ends up in a dispatch queue’s autorelease pool as those pools may not be drained for some time after executing your code. The intermediate versions of data in the Realm file cannot be reused until the RLMRealm object is deallocated. To avoid this issue, you should use an explicit autorelease pool when accessing a Realm from a dispatch queue.

Initializing Swift properties using Realm APIs

If you are writing a Swift application, your app’s classes and structs might be defined with properties whose values are initialized using Realm APIs. For example:

class SomeSwiftType {
    let persons = RLMPerson.allObjects(in: RLMRealm.default())
    // ...
}

If you do define types with such properties, you should note that you may run into problems if such initialization code is called before you have completed setting up your Realm configurations. For example, if you set a migration block for the default Realm configuration in applicationDidFinishLaunching(), but you create an instance of SomeSwiftType before applicationDidFinishLaunching() has run and your Realm requires a migration, you’ll be accessing your Realm before it’s been correctly configured.

In order to avoid such issues you may choose to:

  1. Defer instantiation of any type that eagerly initializes properties using Realm APIs until after your app has completed setting up its Realm configurations.
  2. Define your properties using Swift’s lazy keyword. This allows you to safely instantiate such types at any time during your application’s lifecycle, as long as you do not attempt to access your lazy properties until after your app has set up its Realm configurations.
  3. Only initialize your properties using Realm APIs that explicitly take in user-defined configurations. This way, you can be sure that the configuration values you are using have been set up properly before they are used to open Realms.

Encrypted Realms and multiple processes

Encrypted Realms cannot be accessed by multiple processes simultaneously. This includes iOS Extensions. To work around this, use unencrypted Realms, which can be shared across processes. You can make use of the Security and CommonCrypto system frameworks to encrypt and decrypt data stored in NSData properties on Realm objects.

We’re tracking lifting this limitation in both the Realm Cocoa issue tracker (#1693) and the Realm Core issue tracker (#1845).

Recipes

We’ve put together some recipes showing how to use Realm to accomplish a few specific tasks. We add more recipes regularly, so check back often. If there’s an example you’d like to see, please open an issue on GitHub.

FAQ

How can I find and view the content of my Realm file(s)?

This SO question describes where to find your Realm file. You can then view the content with our Realm Studio.

How big is the Realm base library?

Realm should only add around 5 to 8 MB to your app’s download size. The releases we distribute are significantly larger because they include support for the iOS, watchOS and tvOS simulators, some debug symbols, and bitcode, all of which are stripped by the App Store automatically when apps are downloaded.

Is Realm open source?

Yes! Realm’s internal C++ storage engine and the language SDKs over it are entirely open source and licensed under Apache 2.0. Realm also optionally includes a closed-source synchronization component, but that is not required to use Realm as an embedded database.

I see a network call to Mixpanel when I run my app

Realm collects anonymous analytics when your app is run with a debugger attached, or when it runs in a simulator. These analytics are completely anonymous and help us improve the product by flagging which versions of Realm, iOS, macOS, or which language you target and which versions we can deprecate support for. This call does not run when your app is in production, or running on your user’s devices, only from inside your simulator or when a debugger is attached. You can see exactly what we collect and how we collect it, as well as the rationale for doing so, in our source code.

Troubleshooting

Crash reporting

We encourage you to use a crash reporter in your application. Many Realm operations could potentially fail at runtime (like any other disk I/O), so collecting crash reports from your application will help identify areas where either you (or us) can improve error handling and fix crashing bugs.

Most commercial crash reporters have the option of collecting logs. We strongly encourage you to enable this feature. Realm logs metadata information (but no user data) when throwing exceptions and in irrecoverable situations, and these messages can help debug when things go wrong.

Reporting Realm issues

If you’ve found an issue with Realm, please either file an issue on GitHub or email us at help@realm.io with as much information as possible for us to understand and reproduce your issue.

The following information is very useful to us:

  1. Goals.
  2. Expected results.
  3. Actual results.
  4. Steps to reproduce.
  5. Code sample that highlights the issue (full Xcode projects that we can compile ourselves are ideal).
  6. Version of Realm / Xcode / macOS.
  7. Version of involved dependency manager (CocoaPods / Carthage).
  8. Platform, OS version, and architecture on which the bug happens (e.g. 64-bit iOS 8.1).
  9. Crash logs and stack traces. See Crash Reporting above for details.

Dependency managers

If you’ve installed Realm via CocoaPods or Carthage and you’re experiencing build errors, then it’s likely that you’re either using an unsupported version of that dependency manager, Realm’s integration into the project didn’t succeed, or part of your build tools have stale caches. If that is the case, please try removing the folders the dependency manager created and installing again.

You can also try deleting derived data and cleaning the build folder in Xcode; this can fix issues caused by updating build tool versions or making changes to your project setup such as adding a new target, sharing dependencies across targets, etc.

To clean the build folder, hold down the ‘Option’ key while opening the ‘Product’ menu, then choose ‘Clean Build Folder…’. You can also type ‘Clean’ into the Xcode help search menu and select the ‘Clean Build Folder…’ menu item when it shows up in the search results.

CocoaPods

Realm can be installed via CocoaPods 0.39.0 or greater.

If you have troubles with your CocoaPods integration, it might help to reset the integration state. To achieve that simply run the following commands in Terminal out of your project directory:

pod cache clean Realm
pod cache clean RealmSwift
pod deintegrate || rm -rf Pods
pod install --verbose
rm -rf ~/Library/Developer/Xcode/DerivedData

You can also use cocoapods-deintegrate instead of deleting the Pods directory. With CocoaPods 1.0, this comes as preinstalled plugin. If you’re using an older version, you may consider installing it by gem install cocoapods-deintegrate. You can run it by pod deintegrate. That removes all traces of CocoaPods from your Xcode project.

Carthage

Realm can be installed via Carthage 0.9.2 or later.

To remove all Carthage-managed dependencies from your project, simply run the following commands in Terminal out of your project directory:

rm -rf Carthage
rm -rf ~/Library/Developer/Xcode/DerivedData
carthage update

Realm Core binary fails to download

When building Realm, part of the process includes downloading the core library as a static binary and integrating it into the realm-cocoa project. It’s been reported that in certain instances, the core binary fails to download with the following error:

Downloading core failed. Please try again once you have an Internet connection.

This error can occur due to any of the following reasons:

  1. Your IP address range is from a region that is on the list of United States embargoes. In order to comply with U.S. law, Realm has not been made available in that region. For more information, please see our license.
  2. You are located in mainland China, and due to the country-wide firewall are not able to properly access CloudFlare or Amazon AWS S3 services at the moment. Please see this Realm-Cocoa Issue for more information.
  3. Amazon AWS S3 could be experiencing service issues. Please check the AWS Service Health Dashboard and try again later.

Operating with low memory constraints

If you’d like to use Realm in a context with little available memory, such as a watchOS app or App Extension, we recommend that you specify the classes to be managed by a Realm explicitly in order to avoid a costly call to objc_copyClassList():

RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.objectClasses = @[Dog.class, Person.class];
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];

Getting help

  • Need help with your code? Ask on StackOverflow. We actively monitor & answer questions on SO!
  • Have a bug to report? Open an issue on our repo. If possible, include the version of Realm, a full log, the Realm file, and a project that shows the issue.
  • Have a feature request? Open an issue on our repo. Tell us what the feature should do, and why you want the feature.

If you’re using a crash reporter (like Crashlytics or HockeyApp), make sure to enable log collection. Realm logs metadata information (but no user data) when throwing exceptions and in irrecoverable situations, and these messages can help debug when things go wrong.