Chris
Chris

Reputation: 484

Realm Migration and Optional Properties

I recently upgrade my Realm library from 0.92 (I think) to 0.96.2, but am having some trouble with the new support for ‘optional’ properties. I also need to do a migration for the first time, which is also complicating matters.

The new scheme needed to add a single field to one of the data types, so I coded up a migration that establishes this new property on existing objects:

RLMRealmConfiguration* config = [RLMRealmConfiguration defaultConfiguration];
config.schemaVersion = 1;
config.migrationBlock = ^(RLMMigration* migration, uint64_t oldSchemaVersion)
{
    [migration enumerateObjects:Foo.className
                          block:^(RLMObject* oldObject, RLMObject* newObject) {
                              if (oldSchemaVersion < 1)
                              {
                                newObject[@"user"] = @"";
                              }
                          }];
};
[RLMRealmConfiguration setDefaultConfiguration:config];

However, as soon as the code tries to open a Realm, I get an error message about optional property types:

'Migration is required for object type 'Person' due to the following errors:
- Property 'name' has been made optional.
- Property ‘company’ has been made optional.
- Property 'title' has been made optional.
- Property 'phone' has been made optional.
- Property 'email' has been made optional.
- Property 'homeAddress' has been made optional.'

Question #1 - Since the model is going from ‘required’ properties to ‘optional’, there’s guaranteed to be a value already present for existing objects; so I’m struggling to see why a migration is required.

Question #2 - I’d still like to migrate the objects, and nil out the properties if the string is empty, so I coded up a migration:

RLMRealmConfiguration* config = [RLMRealmConfiguration defaultConfiguration];
config.schemaVersion = 1;
config.migrationBlock = ^(RLMMigration* migration, uint64_t oldSchemaVersion)
{
    NSLog(@"RUNNING REALM MIGRATION");

    // ...basic migration of adding a new property (above)

    [migration enumerateObjects:Person.className
                          block:^(RLMObject* oldObject, RLMObject* newObject) {
                              if (oldSchemaVersion < 1)
                              {
                                  if ([oldObject[@"name"] length] == 0)
                                      newObject[@"name"] = nil;
                                  else
                                      newObject[@"name"] = oldObject[@"name"];

                                  // … repeat for other properties
                }
                          }];
};
[RLMRealmConfiguration setDefaultConfiguration:config];

However, the migration doesn’t appear to be run; a breakpoint inside the if (oldSchemaVersion < 1) block is never hit, and the "RUNNING REALM MIGRATION" message never prints.

The outer block — setting up the RLMRealmConfiguration — is hit inside application:didFinishLaunchingWithOptions:

Upvotes: 0

Views: 2004

Answers (2)

Chris
Chris

Reputation: 484

The issue seems to be that I'm setting up the default RLMRealmConfiguration with my migration info, but [RLMRealm realmWithPath:] ignores the default configuration.

Instead of using realmWithPath:, you can copy the default configuration (including your migrationBlock and schemaVersion), set the path property, and pass it to [RLMRealm realmWithConfiguration:error:]

RLMRealmConfiguration* config = [RLMRealmConfiguration defaultConfiguration];
config.path = file;

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

Upvotes: 0

TiM
TiM

Reputation: 15991

Question #1 By default, all properties were designated 'required' in Realm versions before 0.96. In 0.96, they are marked as 'optional' by default. As such, due to the changes in the new underlying file format to accomodate this (relatively non-trivial change), a migration is required to convert these previously required properties to optional.

If you want to keep these properties as required, you can define this by overriding the [RLMObject requiredProperties] method.

Question 2 Hmm... looking at the sample code, it recommends you encapsulate the enumeration statement inside the if (oldSchemaVersion < 1) conditional block, not the other way around. It's possible that might be causing things to happen out of order. Have you tried swapping that around?

Let me know if that helps! :)

Upvotes: 1

Related Questions