Realm Cocoa 0.87

We just pushed a Realm Objective-C update to this website and to CocoaPods. Here’s what’s new!

RLMResults

RLMArray has been split into two classes: RLMArray and RLMResults. RLMArray is now used only for to-many properties on RLMObject classes, while RLMResults is used for all of the querying and sorting methods. This was done to reflect that the two actually had fairly different APIs (for example, RLMResults does not have addObject:), and they’re expected to diverge further as we add change notifications for queries.

The migration for this should be as simple as replacing RLMArray * with RLMResults * in all of the places that the compiler complains about.

To go with this, arraySortedByProperty:ascending: has been renamed to sortedResultsUsingProperty:ascending:, and addObjectsFromArray: has been renamed to addObjects: to reflect the fact that you can pass any enumerable object to it (such as NSArray, RLMArray, or RLMResults).

// Before
RLMArray *objects = [[UserObject allObjects] arraySortedByProperty:@"karma"
                                                         ascending:YES];

// After
RLMResults *objects = [[UserObject allObjects] sortedResultsUsingProperty:@"karma"
                                                                ascending:YES];

Migrations

The API for migrations has also changed a bit. You now call setSchemaVersion:withMigrationBlock: to register a global migration handler which will be called whenever you open a Realm file with a lower schema version. This simplifies working with multiple Realm files, and ensures that the schema version gets set correctly for newly created Realm files.

You can now create and delete objects during migrations using the new createObject:withObject and deleteObject: methods on RLMMigration.

[RLMRealm setSchemaVersion:5 withMigrationBlock:^(RLMMigration *migration,
                                                  NSUInteger oldSchemaVersion) {
    // Version 5 added support for multiple users, so add a User object for
    // the implicit User in previous versions and associate all existing data
    // with it
    if (oldSchemaVersion == 4) {
        User *user = [migration createObject:@"User"
                                  withObject:@{@"name": @"Default User"}];
        [migration enumerateObjects:@"Message" block:^(RLMObject *oldObject,
                                                       RLMObject *newObject) {
            [user.messages addObject:newObject];
        }];
    }
}];

In-memory realms

In-memory realms have been redesigned to be more useful and work when shared between threads. You now obtain an in-memory Realm by calling +[RLMRealm inMemoryRealmWithIdentifier:], where the identifier is an arbitrary string. Much as with persisted realms, each thread which wants to access the Realm should call inMemoryRealmWithIdentifier: with the same identifier to get a local reference to the in-memory Realm.

Because the data for in-memory Realms is never persisted to disk, it remains valid only as long as you hold a reference to a RLMRealm object for that Realm. As this combined confusingly with the default Realm, the default Realm can no longer be made in-memory.

@interface MyViewController : UITableViewController<UITableViewDataSource>
@property (nonatomic, strong) RLMRealm *realm;
@property (nonatomic, strong) RLMNotificationToken *notification;
@property (nonatomic, strong) RLMResults *data;
@end

@implementation MyViewController
- (void)viewDidLoad {
    [super viewDidLoad];

    self.realm = [RLMRealm inMemoryRealmWithIdentifier:kRealmId]
    self.data = [DemoObject allObjectsInRealm:self.realm];
    self.notification = [self.realm addNotificationBlock:^(NSString *notification,
                                                           RLMRealm *realm) {
        // No need to update self.data as it's automatically updated after each
        // write transaction is committed (and not until it's committed)
        [self.tableView reloadData];
    }];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.data.count;
}

- (void)doStuff {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        RLMRealm *realm = [RLMRealm inMemoryRealmWithIdentifier:kRealmId]
        [realm beginWriteTransaction];

        // freely modify the data without having to copy everything first
        // ...

        // triggers a notification on the main thread which updates the UI with
        // the new data
        [realm commitWriteTransaction];
    });
}
@end

Transaction rollback

Write transactions can now be cancelled and their modifications rolled back with the method -[RLMRealm cancelWriteTransaction]. This reverts all changes made to objects, restores deleted objects, and deletes newly created objects.

[realm beginWriteTransaction];
DogObject *newDog = [DogObject createInDefaultRealmWithObject:@{@"name": @"Rex"}];

// createOwnerForDog:error: might fail, in which case we don't want the
// newly created Dog object to be saved
OwnerObject *owner = [self createOwnerForDog:newDog error:&error];
if (owner) {
    [realm addObject:owner];
    [realm commitWriteTransaction];
}
else {
    NSLog(@"Failed to create owner object: %@", error);
    // Deletes newDog from the Realm
    [realm cancelWriteTransaction];
}

Partial updates with create or update

createOrUpdateInRealm:withObject: and friends now support partially updating existing objects. Passing a dictionary with keys set for only some of the fields will leave the existing values alone for all other fields if the object exists already (or use the default values if it does not). Naturally the primary key field must always be present, of course.

// Set the email field of the existing User object with primary key `key` to
// `newEmail`, and leave the rest of the properties unchanged
[User createOrUpdateInDefaultRealmWithObject:@{@"key": key, @"email": newEmail}];

Query improvements

RLMArray and RLMResults can now be sorted by multiple properties at once using sortedResultsUsingDescriptors:, which takes an NSArray of RLMSortDescriptors describing which properties to sort on.

RLMArray properties can now be queried using objectsWhere: and objectsWithPredicate:.

Querying a sorted RLMResults object now returns results with the same sort order.

Fixed crashes on IN clauses with thousands of items and queries for equality on indexed NSString properties.

!= is now supported on one-to-one relationship properties.

Swift on OS X

Now that Xcode 6.1 is out, the precompiled binaries once again support Swift on OS X.

Assorted other things

RLMRealm now has a deleteAllObjects method to easily clear the Realm file.

Creating RLMRealm instances on background threads is now significantly faster.

Fixed a case where rearranging the properties on an existing RLMObject class didn’t work correctly.

Fixed a crash when assigning to a RLMArray property on a non-persisted RLMObject instance.

Added error messages when a RLMArray property with an invalid protocol or a RLMObject subclass nested within another Swift class are encountered.

Passing an object already persisted in a Realm to addObject: is once again a no-op rather than an error. Trying to add a persisted object to a different Realm is still an error.


Thanks for reading. Now go forth and build amazing apps with Realm! As always, we’re around on the mailing list, StackOverflow, GitHub, twitter and email.