I have Version 1 of my core-data database. (Reduced example)
I make some changes to the model, making V2. This involves creating a new entity with type attribute plus a few others. The type attribute is the link to the plate entity.
The new version of my app is released and the data migrates ok as this is lightweight. For my own reasons at the time, I did not create a relationship at this time.
Sometime later I decide to do some bigger changes to the structure, creating new entities FixtureType and PlateTypeImage. I then create some relationships. This gives me V3 of my model.
Because of the nature of this modification I need to do heavyweight migration from V2 to V3 which involves copying attribute data, populating new attributes and setting the relationships as I go. So, I set up a mapping model, create my necessary migration policies and hit the go button.
This works for V2 to V3 but when testing a migration from V1 to V3, I get a series of errors..., such as
reason=Cannot migrate store in-place: Validation error missing attribute values on mandatory destination relationship
I'm using the following PSC options:
NSDictionary *options = @{
NSMigratePersistentStoresAutomaticallyOption : @YES,
NSInferMappingModelAutomaticallyOption : @YES
};
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {
So, my query is how does core-data migrate the data? Is it sequentially, so I get a lightweight migration from V1 to V2, then heavyweight V2 to V3, or does it do a migration from V1 to V3? If so, do I need to create a migration policy for V1 to V3 (making things unwieldy very quickly to cover every combination)?
Also, have I now lost the lightweight migration facility, once I start using heavyweight?
Advice and comments appreciated.
Core Data versioning is not temporal. It only knows source and destination. Therefore, as soon as you introduce a new model you need to test from all previous models to the current model.
If you add V4, then you need to test for:
V1->V4
V2->V4
V3->V4
So if your V4 requires a heavy migration then you need to do a map for each possible migration.
My general recommendation is to avoid heavy migrations at all costs. They are not designed to work on iOS and frequently cause issues. There are alternatives that will perform better.
Better Approaches
Two most common approaches I use instead of heavy migration is
Export/Import. I use JSON for this. It is easier on memory and therefore can avoid crashes due to memory constraints.
Clever pre and post migration code. For example, if you are splitting data out to a new object, something that requires a heavy migration, you could create the new object and leave the data in the old object. This would turn it into a lightweight migration. From there you could watch for the migration and then move the data manually after the migration is complete. You could even then do another lightweight migration to a final model that doesn't include the soon to be removed attributes.
Keep in mind that iCloud doesn't allow heavy migration so if you are ever going to consider using iCloud then you must skip heavy migration. This is also a very strong indicator that Apple is slowing deprecating heavy migration and leaving it in place as a "last resort" maneuver.