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:
- All indexes will be recreated (only applies to ints and strings).
- All
NSString
,NSDate
, andNSData
properties are converted to nullable, unless marked as required in+requiredProperties
. - 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 onRLMRealm
/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 anObject
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.