Realm-cocoa: Adding a property, then in a future release renaming that model, causes crash when upgrading across multiple versions

Created on 22 Jan 2018  路  4Comments  路  Source: realm/realm-cocoa

Steps to Reproduce

  • In v0.1, have a model Cat with just one property, name
  • In v0.2, add a field hasClaws to Cat model (no explicit migration code needed)
  • In v0.3, rename Cat model to Feline with this migration:
[migration enumerateObjects:@"Cat"
                      block:^(RLMObject *oldObject, RLMObject *newObject) {
                          [migration createObject:Feline.className withValue:oldObject];
                      }];
  • try to upgrade directly from v0.1 to v0.3 -> Crash *** Terminating app due to uncaught exception 'RLMException', reason: 'Invalid property name 'hasClaws' for class 'Cat'.'

Goals

Allow for field additions followed by class renames, without crashing for multi-version upgrades (e.g. skipping from v0.1 to v0.3)

Expected Results

If a property was added in v0.2 then the class was renamed in v0.3, it shouldn't crash upgrading from v0.1 -> v0.3

Actual Results

Crash *** Terminating app due to uncaught exception 'RLMException', reason: 'Invalid property name 'hasClaws' for class 'Cat'.'

Version of Realm and Tooling

$ cat Podfile.lock | grep Realm
  - Realm (2.10.1):
    - Realm/Headers (= 2.10.1)
  - Realm/Headers (2.10.1)
  - Realm (~> 2.10)

Xcode version: Version 9.2 (9C40b)

iOS/OSX version: confirmed on 11.0, but I think it'll happen on any iOS version

Dependency manager + version:

$ pod --version
1.3.1
O-Community T-Duplicate

Most helpful comment

I'm not sure that there's a great solution to this (other than getting #2491 to actually work). The lack of information about intermediate schema versions that a specific install skipped over makes a lot of things in migrations awkward.

An awkward solution would be to do something like the following:

[migration createObject:Feline.className withValue:@{@"name": oldObject.name,
                                                     @"hasClaws": oldObject.objectSchema[@"hasClaws"] ? oldObject[@"hasClaws"] : @NO}]

i.e. provide a dictionary with all of the expected keys, and manually handle missing properties in oldObject.

All 4 comments

This it related to https://github.com/realm/realm-cocoa/issues/2491 which has been stale for ~18 months AFAICT. Would be a nice feature of the renameModelFrom:to: function that it would handle cases like this automatically 馃槃

I work with @tsheaff and we realized there were a couple issues at play.

In the enumerateObjects block, newObject is nil if the file that represents that Realm object no longer exists. That's why we were using oldObject. However... oldObject doesn't contain the property change of hasClaws if we're migrating from an even older version.

Albeit hacky, our solution is to maintain the original Cat model with hasClaws property and use it strictly for the realm migration. This lets us use newObject which has the new property and won't cause the crash we saw above. Everywhere else in our codebase only interacts with Felines

Ideally, we wouldn't have to keep a dead file around.

I'm not sure that there's a great solution to this (other than getting #2491 to actually work). The lack of information about intermediate schema versions that a specific install skipped over makes a lot of things in migrations awkward.

An awkward solution would be to do something like the following:

[migration createObject:Feline.className withValue:@{@"name": oldObject.name,
                                                     @"hasClaws": oldObject.objectSchema[@"hasClaws"] ? oldObject[@"hasClaws"] : @NO}]

i.e. provide a dictionary with all of the expected keys, and manually handle missing properties in oldObject.

Tracked in #2491. Closing this.

Was this page helpful?
0 / 5 - 0 ratings