Recently, I have made quite extensive changes to my schema and need to migrate my CoreData + CloudKit model to a new version. The changes require me to use a custom NSEntityMigrationPolicy
because I have a rather complex mapping between some old entities and new entities.
NSEntityMigrationPolicy
with createDestinationInstances(forSource:in:manager:)
and createRelationships(forDestination:in:manager:)
.storeDescription.setOption(false as NSNumber, forKey: NSMigratePersistentStoresAutomaticallyOption)
storeDescription.setOption(false as NSNumber, forKey: NSInferMappingModelAutomaticallyOption)
I mostly followed a Core Data Heavyweight Migration guide. (It's not for CloudKit specifically.)
When I run my app, I am getting the most basic of errors:
The managed object model version used to open the persistent store is incompatible with the one that was used to create the persistent store.
So, I guess that migration wasn't even attempted, which makes sense because I set NSMigratePersistentStoresAutomaticallyOption
to false.
I tried to go beyond what I could find on the web and attempted to manually initialize migration:
let sourceModel = container.persistentStoreCoordinator.managedObjectModel
guard let modelURL = Bundle.main.url(forResource: "MyModelName", withExtension: "xcdatamodeld") else {
fatalError("Unable to locate model file.")
}
guard let destinationModel = NSManagedObjectModel(contentsOf: modelURL) else {
fatalError("Unable to load destination model.")
}
let migrationManager = NSMigrationManager(sourceModel: sourceModel, destinationModel: destinationModel)
let mappingModel = NSMappingModel(from: [Bundle.main], forSourceModel: sourceModel, destinationModel: destinationModel)!
migrationManager.currentEntityMapping.entityMigrationPolicyClassName = "MyMigrationPolicyClassName"
I am then stuck at the migrateStore(from:type:mapping:to:type:)
method. It seems to target only local storage, not CloudKit. Otherwise, what do I provide for URLs and types?
My question is, how do I implement custom logic for CoreData with CloudKit migration?
I managed to solve the issue by adapting progressive Core Data migration model as explained by William Boles.
This implementation works with NSPersistentCloudKitContainer
. As far as I know, you do not need to set storeDescription.type
for the container because NSPersistentCloudKitContainer
manages the stores under the hood.
After loading the stores, you will need to initialize the schema, like so:
do {
try self.container.initializeCloudKitSchema(options: [])
} catch let error {
fatalError("###\(#function) failed to initialize CloudKit schema due to error: \(error.localizedDescription)")
}
This code will not work if your development device/simulator is not logged into iCloud.
Then, you will also need to open the CloudKit console and deploy your schema to production.