Search code examples
ioscore-datacloudkitmapping-model

CoreData lightweight migration crashes, even after adding a mapping model


In my CoreData model I created an abstract entity (IDManagedObject) to share a common attribute for my other entities.

When I first ran it I got several Cannot merge multiple root entity source tables into one destination entity root table errors.

The complete error is as follows:

Fatal error: Unresolved error Error Domain=NSCocoaErrorDomain Code=134110 "An error occurred during persistent store migration." UserInfo={sourceURL=file:///Users/koen/Library/Developer/CoreSimulator/Devices/8E2C0F01-ABF3-4414-A01A-EE4FFEF8D187/data/Containers/Data/Application/B7A9A4EE-EB57-434F-B1A9-576DCD80CFBD/Library/Application%20Support/MyApp.sqlite, reason=Cannot migrate store in-place: Cannot merge multiple root entity source tables into one destination entity root table, destinationURL=file:///Users/koen/Library/Developer/CoreSimulator/Devices/8E2C0F01-ABF3-4414-A01A-EE4FFEF8D187/data/Containers/Data/Application/B7A9A4EE-EB57-434F-B1A9-576DCD80CFBD/Library/Application%20Support/MyApp.sqlite, NSUnderlyingError=0x6000019fb810 {Error Domain=NSCocoaErrorDomain Code=134110 "An error occurred during persistent store migration." UserInfo={message=Cannot merge multiple root entity source tables into one destination entity root table, destinationRootEntity=IDManagedObject, NSUnderlyingException=Cannot merge multiple root entity source tables into one destination entity root table, sourceRootEntities=(
    Book,
    Author
), reason=Cannot merge multiple root entity source tables into one destination entity root table}}}, ["NSUnderlyingError": Error Domain=NSCocoaErrorDomain Code=134110 "An error occurred during persistent store migration." UserInfo={message=Cannot merge multiple root entity source tables into one destination entity root table, destinationRootEntity=IDManagedObject, NSUnderlyingException=Cannot merge multiple root entity source tables into one destination entity root table, sourceRootEntities=(
    Book,
    Author
), reason=Cannot merge multiple root entity source tables into one destination entity root table}, "destinationURL": file:///Users/koen/Library/Developer/CoreSimulator/Devices/8E2C0F01-ABF3-4414-A01A-EE4FFEF8D187/data/Containers/Data/Application/B7A9A4EE-EB57-434F-B1A9-576DCD80CFBD/Library/Application%20Support/MyApp.sqlite, "sourceURL": file:///Users/koen/Library/Developer/CoreSimulator/Devices/8E2C0F01-ABF3-4414-A01A-EE4FFEF8D187/data/Containers/Data/Application/B7A9A4EE-EB57-434F-B1A9-576DCD80CFBD/Library/Application%20Support/MyApp.sqlite, "reason": Cannot migrate store in-place: Cannot merge multiple root entity source tables into one destination entity root table]: file MyApp/SceneDelegate.swift, line 96

Interestingly, it only shows the error for those two entities, but I have many more.

After doing a bit of searching I found I need to add a mapping model to migrate between the old and new version. So I added that via Cmd-N in Xcode and selecting the source and destination model.

But I am still getting the error.

Both shouldMigrateStoreAutomatically and shouldInferMappingModelAutomatically are set to true.

What am I missing here?

The old model is in the current app written in Objective-C, the new model is in a new version of the app written is Swift and now also uses CloudKit. Using Xcode 12.2.

I am happy to add more info if needed, just don't know what would be relevant for the question.


Solution

  • The short version is that automatic lightweight migration can't make this change, even with a mapping model. You need to do a full manual migration to make this change. If you check out Apple's lightweight migration documentation, it says that

    ...if two existing entities do not share a common parent in the source, they cannot share a common parent in the destination.

    This is what you're trying to do, but it can't happen automatically.

    The longer answer to why gets into some details of how Core Data works. Xcode doesn't do a good job of making this obvious.

    • When you have two or more entities without a common parent entity, Core Data treats them as completely independent. Your SQLite has a Book table, an Author table, and so on.
    • When you have two or more entities with a common parent, Core Data treats them as special cases of the same table. Your SQLite has an IDManagedObject table. Core Data uses other logic to decide if an entry in that table is a Book or an Author or something else.

    Because of this, what you're actually doing is merging more than one entity into a single new entity. Core Data doesn't know how to do that automatically, which is what it's telling you in that error message.

    Core Data is only complaining about those two tables because they're the first ones it sees. It can't handle merging any of them, and it only mentions two tables because it's giving up before looking at the rest.

    To make this change, you'll need to create a migration manager and write code to do the migration. Apple describes this in Customizing the Migration Process. Or, depending on what exactly you need, there might be other solutions that don't require adding a new parent entity.