Updating Core Data object ID URIs on store migrations

I found out recently that Core Data object IDs can change. This usually happens if you migrate a store, such during a heavyweight migration (lightweight migrations don’t count, as they mutate in place). If you were like me and persisting object IDs, then you’ll have to update those URIs, or you will have a very bad day trying to fetch objects from these IDs.

The easiest way to do this is to lean on NSMigrationManager, because it has both the source and destination model versions, and can get the destination object version from the source version. If you’re manually doing the migration for model upgrades, you easily have it already. If you want to update it from a NSEntityMigrationPolicy, then the easiest option is to stash the NSMigrationManager by overriding beginEntityMapping:manager:error:. You provide it the source NSManagedObjects, and out comes the destination version’s managed objects, as shown below, of which you can get the new object IDs to persist in the new version of the model:

var manager: NSMigrationManager!

private func destinationURIsFromSourceObjects(_ objects: [NSManagedObject]) -> [URL] {
    return manager.destinationInstances(forEntityMappingName: "TrackToTrack", sourceInstances: objects).map {
        $0.objectID.uriRepresentation()
    }
}

Note that you need to provide the entity mapping name – regardless if your entities heavily changed or not. For the Xcode mapping model generator, check the name – it’s usually something like TrackToTrack. If you need to get the managed objects from the source version’s URIs, you can use the the sourceContext property in the migration manager to get the source model version’s managed object context.