Introducing new attributes or entities in your data model requires that you create a new model version and, assuming the changes are not too complex, requires Core Data to perform a lightweight migration to upgrade the data in the old store to the new model.
Unfortunately it seems that when you have an OSX Core Data iCloud store with data imported from multiple peer devices and you try and perform a lightweight migration Core Data gets confused and does not correctly migrate all the data to the new store.
Fortunately this seems to work OK on iOS even though it takes considerable time to complete.
Lightweight migration of local stores on the other hand seems to work pretty flawlessly. So it might be an idea to get your user to switch to using a local store before upgrading your app.
Ideally I want to implement a workflow that will automatically migrate the iCloud store to a local store, perform the upgrade and then migrate the upgraded store to iCloud.
OK here is how I have done this – no need to keep two copies of the model because it is possible to get the current model using the following call
_sourceModel = [NSManagedObjectModel mergedModelFromBundles:nil forStoreMetadata:metaData];
So as long as we use this _sourceModel we can open the existing store without any fear that an automatic lightweight migration will be performed by Core Data.
Here is a brief summary
On startup in
we call the
OSCDStackManager.checkCDModelVersion method which determines if an upgrade is required and if so also gets the source model using the call described above. If an upgrade is required we display an upgrade view while the upgrade is being performed instead of the main view. Once the upgrade is completed we then display the usual main view.
UpgradeViewController we then perform the upgrade by going through the following key steps:
1. Set a bunch of flags so reused methods know not to perform certain actions, such as loading seed data because we are performing an upgrade. Migrate the store to a local file by calling
2. Remove the iCloud store (to prevent any issues when migrating the upgraded local store back to iCloud. Actually this gets done in step1 above already so this is a empty method (I got too lazy to separate the code for now). You guessed it call
3. Now open the migrated store using the new model to cause Core Data to perform a lightweight migration. Close it. All done in
4. Migrate the upgraded store back to iCloud – make sure there is nothing in iCloud first !! Potential for problems here if other devices are running the app live while this is going on so best to tell the user to delete the app from other devices before starting this upgrade.
5. Start using the App.
Not too hard after all.