Realm Blog

Realm Objective-C & Swift 0.96

Realm files read or written by this version cannot be opened with previous versions of Realm. Please be careful about shipping updates to your existing apps!

We just released version 0.96 of Realm Objective-C and Realm Swift.

This release contains official support for null properties, keypath collection queries (count/min/max/sum/avg), a new RealmCollectionType protocol in Swift, error handling in writes, and a fistful of bug fixes!

Last week, we published a beta version of this release but now we feel this is ready for prime time.

Null Support

This version adds support for null values for all property types.

Objective-C

NSString *, NSDate *, NSData * now allow nil by default. You can disallow setting a property to nil by overriding the +requiredProperties class method and including the names of the properties which you want to disallow nil for. Accessing a Realm file created with a previous version will automatically convert these properties to nullable in the file itself, unless explicitly marked not to do so in +requiredProperties.

Optional numbers can be stored using an NSNumber * property which is tagged with the type of the number. You can use NSNumber<RLMInt> *, NSNumber<RLMBool> *, NSNumber<RLMFloat> *, and NSNumber<RLMDouble> *.

@interface OptionalTypes : RLMObject
@property NSString *optionalString;
@property NSString *requiredString;
@property NSData *optionalData;
@property NSDate *optionalDate;
@property NSNumber<RLMInt> *optionalInt;
@property NSNumber<RLMBool> *optionalBool;
@property NSNumber<RLMFloat> *optionalFloat;
@property NSNumber<RLMDouble> *optionalDouble;
@end
@implementation OptionalTypes
+ (NSArray *)requiredProperties {
    return @[@"requiredString"];
}
@end

Swift

String, NSDate and NSData properties work as you’d expect them to and can either be optional or non-optional. Unfortunately, we’re unable to directly support optional numeric types (see #2147), so you have to wrap the types in RealmOptional. For example:

class OptionalTypes: Object {
    dynamic var string: String? = "B"
    dynamic var data: NSData? = "C".dataUsingEncoding(NSUTF8StringEncoding)
    dynamic var date: NSDate? = NSDate(timeIntervalSince1970: 10)
    let int = RealmOptional<Int>(1)
    let float = RealmOptional<Float>(2.2)
    let double = RealmOptional<Double>(3.3)
    let bool = RealmOptional<Bool>(true)
    let boolWithNilDefault = RealmOptional<Bool>(nil)
}

As with List properties, RealmOptional properties should always be declared with let and not be declared dynamic. The actual value of the RealmOptional is read from and written to the value property.

Automatic Conversion

Realm files opened with this version will be automatically converted to the new file format on first access, even when only accessed in a read transaction. This automatic conversion will perform the following operations:

  1. All indexes will be recreated (only applies to ints and strings).
  2. All NSString, NSDate, and NSData properties are converted to nullable, unless marked as required in +requiredProperties.
  3. The new format version number will be written to the file.

Since this writes to the file, Realm files created with a previous version of Realm won’t be able to be read with this new version. If you’re bundling a Realm file in your app, it will have to be updated to use this new file format.

Note that this format conversion is irreversible.

Manual Migration

Please note that even though the file format upgrade happens seamlessly in the background, a schema migration in order to adopt nullable properties still needs to be performed. Even if you haven’t made any changes to your object schema in the interim, certain properties are now made nullable by default, and so you may be required to make changes to your code if you want to keep the schema the same as before. That’s especially the case when you have NSString, NSDate and NSData properties used from Objective-C, which are - as explained as above - optional by default. So assuming you had a model like shown below originally:

@interface Dog : RLMObject
@property NSString *name;
@property NSDate *birthdate;
@property Person *owner;
@end

@implementation Dog
@end

As a dog owner, you usually know your dog’s name - clearly a required property - but if you may have adopted it, you might not know the exact birthdate - something that can be considered optional. In this case, you would need to define the name as a required property in an overridden implementation of +requiredProperties on the class Dog as shown here.

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

Since at this point birthdate is now implicitly optional, you will still need to define a basic migration in order to properly update the schema to accomodate this. In the past, you may have provided arbitrary data to serve as default values, such as [NSDate dateWithTimeIntervalSince1970:0], which is admittedly very ambiguous in relation to birthdates, but as this is no longer necessary, you may want to go back any of your existing objects to make sure you remove any arbitrary values and properly replace them with null ones.

// 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,
// so assuming that'd be your first migration, we set it to 1.
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) {
        [migration enumerateObjects:Person.className
                          block:^(RLMObject *oldObject, RLMObject *newObject) {
            // convert null-placeholders to actual nils
            newObject[@"birthdate"] = [oldObject[@"birthdate"] timeIntervalSince1970] > 0 ? oldObject[@"birthdate"] : nil;
        }];
        
        // Note: Even if you don't have to convert placeholder values,
        // you still have to provide at least an empty migration block
        // when your schema has changes to nullability of properties.
    }
};

The first access of [RLMRealm defaultRealm] will cause the file format to be upgraded automatically, and will execute the migration.

Keypath Collection Queries

Keypath collection queries using @count, @min, @max, @sum and @avg are now supported on RLMArray/List properties. See our handy NSPredicate Cheatsheet for more details on how to use these.

RealmCollectionType

RealmCollectionType is deprecated and has been replaced by RealmCollection. The section below is retained in its original state for posterity, but you should see here for the latest on `RealmCollection`.

Users of Realm Objective-C have had the ability to represent both RLMArray and RLMResults using their shared protocol (RLMCollection) for a long time. We’re now reflecting that protocol in Swift, while expanding what it can do.

Functionality common to both List and Results is now declared in a RealmCollectionType protocol that both types conform to. A type-erased wrapper is also provided, so Swift can store a reference to the underlying collection despite its layout being different:

class ViewController: UIViewController {
    var collection: AnyRealmCollection

    init(collection: RealmCollectionType) {
        super.init()
        self.collection = AnyRealmCollection(collection)
    }
}

Improved Error Handling

In current versions of Realm committing a write transaction on a severely space-constrained device would cause Realm to crash. Thankfully, Realm’s crash safety would prevent existing data from being corrupted, but this wasn’t exactly a solid user experience.

Now committing write transactions via commitWrite / commitWriteTransaction and write / transactionWithBlock optionally allow for handling errors when the disk is out of space.

Minor Enhancements

  • Added isEmpty property on RLMRealm/Realm to indicate if it contains any objects.

Bug Fixes

  • Fix assertion failure when inserting NSData between 8MB and 16MB in size.
  • Fix assertion failure when rolling back a migration which removed an object link or RLMArray/List property.
  • Add the path of the file being opened to file open errors.
  • Fix crashes when the first Realm opened uses a class subset and later Realms opened do not.
  • Fix inconsistent errors when Object(value: ...) is used to initialize the default value of a property of an Object subclass.
  • Throw an exception when a class subset has objects with array or object properties of a type that are not part of the class subset.
  • Fix a crash that could be triggered by rapidly opening and closing a Realm many times on multiple threads at once.
  • Fix several places where exception messages included the name of the wrong function which failed.

Thanks for reading. Now go forth and build amazing apps with Realm! As always, we’re around on StackOverflow, GitHub, or Twitter.


Realm Team

At Realm, our mission is to help developers build better apps faster. We provide a unique set of tools and platform technologies designed to make it easy for developers to build apps with sophisticated, powerful features — things like realtime collaboration, augmented reality, live data synchronization, offline experiences, messaging, and more.

Everything we build is developed with an eye toward enabling developers for what we believe the mobile internet evolves into — an open network of billions of users and trillions of devices, and realtime interactivity across them all.

Get more development news like this