2

I currently have an app using Core Data in the App Store: the app allows people to record their water and sailing activities (think of it like Strava for sailors). I have not updated the app for 3 years, the app seems to be still working fine on latest iOS versions but I recently planned to improve the app.

I am currently working on an update for this app, and need to change the data model and schema. I would like to have an automatic lightweight migration. I renamed some entities, properties and relationships, but I made sure to put the previous ids in the Renaming ID field in the editor. I want to take advantage of the opportunity to sync the updated schema on CloudKit. I followed the instruction on Apple Developer documentation to setup the sync. I also made sure to initialize the schema using initializeCloudKitSchema(). When I visit the dashboard, I see the correct schema. The container is only in development mode, not pushed into production.

When I launch the app with a sqlite file generated by the available app, it seems the migration works well because the data is still here and correct. I can navigate in the app normally and when I visit the CloudKit dashboard, the data is correctly saved.

But sometimes, the app crashes at launch with the following error:

UserInfo={reason=CloudKit integration forbids renaming 'crewMembers' to 'sailors'.
Older devices can't process the new relationships.
NSUnderlyingException=CloudKit integration forbids renaming 'crewMembers' to 'sailors'.
Older devices can't process the new relationships.}}}
Fatal error: Unresolved error Error Domain=NSCocoaErrorDomain Code=134110 "An error occurred during persistent store migration." 

The concerned entities were renamed, as the relationships and the relationship is a many-to-many, optional on both sides. This is occurring even if I reset the CloudKit development container. I don’t really have a clear idea of when this is appearing (seems random, after I updated some data or after I update the Core Data model). Any idea why the app is crashing? I would like as much as possible to keep the new naming for my entities and relationships.

  • SKPRCrewMemberMO renamed to Sailor
  • SKPRTrackMO renamed to Activity
  • crewMembers <<--->> tracks renamed sailors <<--->> activities

Here are some screenshots of the previous and updated data model for the entity at the origin of the migration issue, as well as some code regarding my Core Data stack initialization and the console error il getting.

enter image description here

enter image description here

enter image description here

enter image description here

PS: the app is used by few hundreds of people. That’s not a lot, but still, some of them have dozens of recorded activities and I don’t want to break anything and lose or corrupt data. I could launch a new app but users would lose their progress as it’s only saved locally in a shared container (app group was used as I wanted to share the Core Data with an Apple Watch extension). And I would lose the user base and App Store related things.

private init() {
    container = NSPersistentCloudKitContainer(name: "Skipper")
    
    guard let description = container.persistentStoreDescriptions.first else {
        fatalError("###\(#function): Failed to retrieve a persistent store description.")
    }
    description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
    description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
    let id = "iCloud.com.alepennec.sandbox20201013"
    let options = NSPersistentCloudKitContainerOptions(containerIdentifier: id)
    description.cloudKitContainerOptions = options
    
    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
        if let error = error as NSError? {
            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
    })
    
    do {
        try container.initializeCloudKitSchema()
    } catch {
        print("Unable to initialize CloudKit schema: \(error.localizedDescription)")
    }
    
    container.viewContext.automaticallyMergesChangesFromParent = true
    container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
}
alpennec
  • 1,864
  • 3
  • 18
  • 25
  • My understanding is that to preserve backward compatibility, CloudKit does not allow the renaming of fields. So you'd need to create new fields rather than rename old ones. The issue being - this is fine for anyone that downloads the new version, but what about those old the old one after fields have been renamed? I'm surprised it doesn't crash every time...though take with a grain of salt as I haven't attempted a similar migration :) – Steve B Oct 16 '20 at 09:50
  • I've maybe found the reason of the crash: when I launch the app with a sqlite file from the previous version (let's say V1), the file is migrated to the last model (let's say V2). But if I make changes to the model again without creating a new Core Data model, the updated sqlite file (V2) can't be opened → I would need to make a new version of the model so the V2 file would migrate to the V3. If I launch the app with a V1 file after I've made some model changes again, the updated model is used to migrate, so no error is encountered. – alpennec Oct 16 '20 at 13:56
  • @alpennec your comments make sense, it seems much safer to create a new model version when you modify the object graph, but was that the solution to your problem? I'm encountering an unusual issue with migration and CloudKit, so wondering whether you had any other insights to offer or an answer to your own question? – andrewbuilder Feb 22 '22 at 05:49

0 Answers0