Core Data - lightweight migrations and multiple core data model files (xcdatamodel) Core Data - lightweight migrations and multiple core data model files (xcdatamodel) ios ios

Core Data - lightweight migrations and multiple core data model files (xcdatamodel)


After attending a WWDC 2012 lab and meeting with the Core Data team, it seems you are forced to put all your model info in a single xcdatamodel. CoreData is not intelligent enough to check its existing stores as a combination of the stores that created it and are still on disk. As C. Roald pointed out, you can do some processing on old xcdatamodel files, but it's quite sad that Core Data does not handle this more elegantly.


I encountered this problem also. I lost several hours trying to figure out WTF -- very frustrating.

I believe the easiest way to solve this problem is:

  1. Pick which model you're keeping -- say ModelB -- and create a new version for it based on the published version. I'll call the published version ModelBv1 and the new version ModelBv1_merge.

  2. Open contents XML files for ModelAv1 and ModelBv1_merge in a text editor (ie, ModelA.xcdatamodeld/ModelAv1.xcdatamodel/contents and ModelB.xcdatamodeld/ModelBv1_merge.xcdatamodel/contents) and merge the XML by hand. The schema is very simple -- just copy the <entity> elements and merge the <elements> element (into the _merge contents file) and you're done.

  3. Open the contents file for your new ModelBv2 and again merge ModelA contents into it.

  4. Remove ModelA from your project file.

Check in Xcode that ModelBv1_merge and ModelBv2 look sane, and contain everything you expect (the union of old Model A and Model B). Build and you should be done.

(I think this has a caveat of "provided both contents files were written by the same version of Xcode", but I think if you have an old contents file it should be easy enough to make Xcode rewrite it by making a trivial change somewhere.)


I have a scenario in which my application model is obtained merging multiple models, and I managed to have a kind of automatic lightweight migration in this way:

NSError* error = nil;NSURL *documentsDirectory = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];NSURL *storeURL = [documentsDirectory URLByAppendingPathComponent:@"db.sqlite"];NSString* storePath = [storeURL path];NSLog(@"Store URL: %@", storeURL);if( [[NSFileManager defaultManager] fileExistsAtPath:storePath] ){    // Load store metadata (this will contain information about the versions of the models this store was created with)    NSDictionary *storeMeta = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:nil URL:storeURL error:&error];    if(storeMeta){        // Get the current model, merging all the models in the main bundle (in their current version)        NSManagedObjectModel* model=[NSManagedObjectModel mergedModelFromBundles:nil];        // If the persistent store is not compatible with such a model (i.e. it was created with a model obtained merging old versions of "submodels"), migrate        if(![model isConfiguration:nil compatibleWithStoreMetadata:storeMeta]){            // Load the old model            NSManagedObjectModel*oldModel = [NSManagedObjectModel mergedModelFromBundles:nil forStoreMetadata:storeMeta];            // Compute the mapping between old model and new model            NSMappingModel* mapping = [NSMappingModel inferredMappingModelForSourceModel:oldModel destinationModel:model error:&error];            if(mapping){                // Backup old store                NSURL* storeBackupURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:[NSString stringWithFormat:@"db.sqlite.%@.bck", [NSDate new]]];                BOOL done = [[NSFileManager defaultManager] moveItemAtURL:storeURL toURL:storeBackupURL error:&error];                if(done){                    // Apply the mapping                    NSMigrationManager* migrationManager = [[NSMigrationManager alloc] initWithSourceModel:oldModel destinationModel:model];                    BOOL done = [migrationManager migrateStoreFromURL: storeBackupURL                                                                 type: NSSQLiteStoreType                                                              options: nil                                                     withMappingModel: mapping                                                     toDestinationURL: storeURL                                                      destinationType: NSSQLiteStoreType                                                   destinationOptions: nil                                                                error: &error];                    if(done){                        NSLog(@"Store migration successful!!!");                    }                }            }        }    }}if(error){    NSLog(@"Migration error: %@", error);}