realm / realm-swift

Realm is a mobile database: a replacement for Core Data & SQLite
https://realm.io
Apache License 2.0
16.32k stars 2.15k forks source link

Multiple migration with optional fields #4612

Closed jdanthinne closed 7 years ago

jdanthinne commented 7 years ago

Goals

I'm trying to write a correct migration path for the following model

Schema version 0

final class Sheet: Object {
    dynamic var title: String = ""
}

Schema version 1

final class Sheet: Object {
    dynamic var title: String = ""
    dynamic var dateStart: Date?
}

Schema version 2

final class Sheet: Object {
    dynamic var title: String = ""
    dynamic var dateStart: Date?
    dynamic var hasDate: Bool = false
}

My migration block is

if oldSchemaVersion < 1 {
    // Nothing to do, dateStart is a new field
}
if oldSchemaVersion < 2 {
    migration.enumerateObjects(ofType: Sheet.className(), { (oldObject, newObject) in
        newObject!["hasDate"] = oldObject!["dateStart"] != nil
    })
}

Expected Results

A correct migration…

Actual Results

For apps migrating from schema version 1 to 2, everything seems ok, because dateStart exists. For apps migrating from schema version 0 to 2, its breaks…

Terminating` app due to uncaught exception 'RLMException', reason: 'Invalid property name 'dateStart' for class 'Sheet'.'
*** First throw call stack:
(
    0   CoreFoundation                      0x0000000110b4bd4b __exceptionPreprocess + 171
    1   libobjc.A.dylib                     0x000000011021b21e objc_exception_throw + 48
    2   Realm                               0x000000010f061ccb RLMDynamicGetByName + 396
    3   RealmSwift                          0x000000010f670970 _TFC10RealmSwift13DynamicObjectg9subscriptFSSGSqP__ + 80
    4   Univers                             0x000000010e98f090 _TFFC7Univers11AppDelegate17runRealmMigrationFT9migrationC10RealmSwift9Migration16oldSchemaVersionVs6UInt64_T_U1_FTGSqCS1_13DynamicObject_GSqS4___T_ + 336
    5   Realm                               0x000000010f072462 -[RLMMigration enumerateObjects:block:] + 464
    6   RealmSwift                          0x000000010f66e833 _TFC10RealmSwift9Migration16enumerateObjectsfT6ofTypeSSFTGSqCS_13DynamicObject_GSqS1___T__T_ + 227
    7   Univers                             0x000000010e98e94f _TFC7Univers11AppDelegate17runRealmMigrationfT9migrationC10RealmSwift9Migration16oldSchemaVersionVs6UInt64_T_ + 847
    8   Univers                             0x000000010e996b4a _TPA + 26
    9   Univers                             0x000000010e98af33 _TTRXFo_oC10RealmSwift9MigrationdVs6UInt64__XFo_iTS0_S1___iT__ + 35
    10  Univers                             0x000000010e98afd1 _TPA__TTRXFo_oC10RealmSwift9MigrationdVs6UInt64__XFo_iTS0_S1___iT__ + 81
    11  RealmSwift                          0x000000010f68e8e4 _TPA__TTRXFo_iTC10RealmSwift9MigrationVs6UInt64__iT__XFo_oS0_dS1___ + 68
    12  RealmSwift                          0x000000010f68ea9c _TPA__TTRXFo_oC10RealmSwift9MigrationdVs6UInt64__XFo_iTS0_S1___iT__ + 60
    13  RealmSwift                          0x000000010f68eace _TPA__TTRXFo_oCSo12RLMMigrationdVs6UInt64__XFo_iTS_S0___iT__ + 14
    14  RealmSwift                          0x000000010f68e8e4 _TPA__TTRXFo_iTC10RealmSwift9MigrationVs6UInt64__iT__XFo_oS0_dS1___ + 68
    15  Realm                               0x000000010f0729e0 -[RLMMigration execute:] + 922
    16  Realm                               0x000000010f0ee542 _ZNSt3__110__function6__funcIZ41+[RLMRealm realmWithConfiguration:error:]E3$_0NS_9allocatorIS2_EEFvNS_10shared_ptrIN5realm5RealmEEES8_RNS6_6SchemaEEEclEOS8_SD_SA_ + 380
    17  Realm                               0x000000010f10de1a _ZNSt3__110__function6__funcIZN5realm5Realm13update_schemaENS2_6SchemaEyNS_8functionIFvNS_10shared_ptrIS3_EES7_RS4_EEEbE3$_1NS_9allocatorISB_EEFvvEEclEv + 362
    18  Realm                               0x000000010f047602 _ZN5realm11ObjectStore20apply_schema_changesERNS_5GroupERNS_6SchemaERyRKS3_yNS_10SchemaModeERKNSt3__16vectorINS_12SchemaChangeENS9_9allocatorISB_EEEENS9_8functionIFvvEEE + 3198
    19  Realm                               0x000000010f10a0a9 _ZN5realm5Realm13update_schemaENS_6SchemaEyNSt3__18functionIFvNS2_10shared_ptrIS0_EES5_RS1_EEEb + 525
    20  Realm                               0x000000010f0eb8ce +[RLMRealm realmWithConfiguration:error:] + 1394
    21  RealmSwift                          0x000000010f681484 _TFC10RealmSwift5RealmcfzT_S0_ + 100
    22  Univers                             0x000000010e98833c _TFC7Univers11AppDelegate11applicationfTCSo13UIApplication29didFinishLaunchingWithOptionsGSqGVs10DictionaryVSC29UIApplicationLaunchOptionsKeyP____Sb + 3484
    23  Univers                             0x000000010e98b5c4 _TToFC7Univers11AppDelegate11applicationfTCSo13UIApplication29didFinishLaunchingWithOptionsGSqGVs10DictionaryVSC29UIApplicationLaunchOptionsKeyP____Sb + 180
    24  UIKit                               0x0000000111e993c2 -[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 290
    25  UIKit                               0x0000000111e9ad47 -[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 4236
    26  UIKit                               0x0000000111ea10ed -[UIApplication _runWithMainScene:transitionContext:completion:] + 1731
    27  UIKit                               0x0000000111e9e26d -[UIApplication workspaceDidEndTransaction:] + 188
    28  FrontBoardServices                  0x00000001189586cb __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 24
    29  FrontBoardServices                  0x0000000118958544 -[FBSSerialQueue _performNext] + 189
    30  FrontBoardServices                  0x00000001189588cd -[FBSSerialQueue _performNextFromRunLoopSource] + 45
    31  CoreFoundation                      0x0000000110af0761 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    32  CoreFoundation                      0x0000000110ad598c __CFRunLoopDoSources0 + 556
    33  CoreFoundation                      0x0000000110ad4e76 __CFRunLoopRun + 918
    34  CoreFoundation                      0x0000000110ad4884 CFRunLoopRunSpecific + 420
    35  UIKit                               0x0000000111e9caea -[UIApplication _run] + 434
    36  UIKit                               0x0000000111ea2c68 UIApplicationMain + 159
    37  Univers                             0x000000010e99562f main + 111
    38  libdyld.dylib                       0x000000011488468d start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

Version of Realm and Tooling

Realm version: 2.4.2 Xcode version: 8.2.1 iOS/OSX version: 10.2/10.12.3 Dependency manager + version: Carthage 0.18.1

pigeon-archive commented 7 years ago

Hi @jdanthinne, thanks for reaching out. I'll have someone review what you've shared and respond with a solution or follow-up questions soon. Cheers! :-)

jdanthinne commented 7 years ago

Thanks!

austinzheng commented 7 years ago

My guess is that if oldSchemaVersion < 2 { applies to both versions 0 and 1, so it's running for the 0-2 migration and not finding the hasDate property on the version-0 objects. I think the easiest way to solve your problem is to move your if statement into the migration block and make it run only when migrating from version 1 to a later version.

migration.enumerateObjects(ofType: Sheet.className(), { (oldObject, newObject) in
  if oldSchemaVersion == 1 {
    newObject!["hasDate"] = oldObject!["dateStart"] != nil
  }
})

You might also want to check out our multi version migration example.

jdanthinne commented 7 years ago

That was simple. Brilliant. Thanks.