Core Data migration problem: "Persistent store migration failed, missing source managed object model."
Two possibilities:
- Your source model in your app does not match the actual store on disk.
- Your mapping model does not match your source model.
Turn on Core Data debugging and you should be able to see the hashes that Core Data is looking for when it is doing the migration. Compare these hashes to what is in your store on disk and see if they match up. Likewise the debugging should let you see the hashes in the mapping model to help you match everything up.
If it is just your mapping model that is misaligned, you can tell it to update from source from the design menu in Xcode. If you are missing the actual source model for your store file on disk then you can look in your version control system or try using an automatic migration to get that file to migrate to the model that you believe is the source.
Update 1
The location for changing the source and destination models has moved to the bottom of the editor window:
Rather than merging all models in the bundle, I've specified the two models I want to use (model 1 and the new version of model 2) and merged them using modelByMergingModels:
This doesn't seem right. Why merge the models? You want to use model 2, migrating your store from model 1.
From the NSManagedObjectModel class reference
modelByMergingModels:
Creates a single model from an array of existing models.
You don't need to do anything special/specific with your source model (model 1).. just so long as it's in your bundle, the automatic lightweight migration process will discover and use it.
I would suggest abandoning the mapping model you created in Xcode, as I've seen terrible performance in comparison with the automatic-lightweight migrations. Your mileage may vary, my changes between models are different to yours, but i wouldn't be surprised. Try some timing with and without your own mapping model in the bundle.
/* Inferred mapping */
NSError *error;
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption,nil];
NSPersistentStore *migratedStore = [persistentStoreCoordinator addPersistentStoreWithType:nil
configuration:nil
URL:self.storeURL
options:options
error:&error];
migrationWasSuccessful = (migratedStore != nil);
You can verify in your code that your source model is available, by attempting to load it and verify that it is not nil:
NSString *modelDirectoryPath = [[NSBundle mainBundle] pathForResource:@"YourModelName" ofType:@"momd"];
if (modelDirectoryPath == nil) return nil;
NSString *modelPath = [modelDirectoryPath stringByAppendingPathComponent:@"YourModelName"];
NSURL *modelFileURL = [NSURL fileURLWithPath:modelPath];
NSManagedObjectModel *modelOne = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelFileURL];
if (modelOne == nil) {
NSLog(@"Woops, Xcode lost my source model");
}
else {
[modelOne release];
}
This assumes in your project you have a resource "YourModelName.xcdatamodeld" and "YourModelName.xcdatamodel" within it.
Also, you can check if that model is compatible with your existing, pre-migration persistent store:
NSError *error;
NSDictionary *storeMeta = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:nil URL:self.storeURL error:&error];
if (storeMeta == nil) {
// Unable to read store meta
return NO;
}
BOOL isCompatible = [modelOne isConfiguration:nil compatibleWithStoreMetadata:storeMeta];
That code assumes you have a method -storeURL
to specify where the persistent store is loaded from.
While attempting to upgrade an existing app's Core Data model (and migrate legacy data), I just ran across a scenario where a third-party Framework was writing data into an app's database. I was getting this error, "Can't find model for source store." Because the third party model was not loaded when I was attempting the migration, the migration was failing.
I wrote this method (below) during troubleshooting this issue. It may be useful to those who are facing these types of issues.
- (BOOL) checkModelCompatibilityOfStoreWithURL: (NSURL *) myStoreURL
forModelNamed: (NSString *) myModelName
withBasePath: (NSString *) myBasePath;
{
NSError * error = nil;
NSDictionary *storeMeta = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:nil URL:myStoreURL error:&error];
if (!storeMeta) {
NSLog(@"Unable to load store metadata from URL: %@; Error = %@", myStoreURL, error);
return NO;
}
NSString * modelPath = [myBasePath stringByAppendingPathComponent: myModelName];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath: modelPath]) {
// uh oh
NSLog(@"Can't find model.");
return NO;
}
NSURL * modelURL = [NSURL fileURLWithPath: modelPath];
NSManagedObjectModel * model = [[[NSManagedObjectModel alloc] initWithContentsOfURL: modelURL] autorelease];
BOOL result = [model isConfiguration: nil compatibleWithStoreMetadata: storeMeta];
NSLog(@"Tested model, %@, is%@ compatible with Database", modelPath, result ? @"" : @" ~not~");
return result;
}
This code snippet will obtain a store's metadata.
NSError *error = nil;
NSURL *storeUrl = [NSURL fileURLWithPath:storePath];
NSDictionary *storeMeta = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:nil URL:storeUrl error:&error];
NSLog(@"%@", [storeMeta objectForKey: @"NSStoreModelVersionHashes"]);
The VersionInfo.plist (stored in the compiled app's bundle) contains the hashes that are associated with the various entities in your models (base64 encoding). Similarly, a BLOB column in the datastore (Z_METADATA.Z_PLIST) contains a binary-encoded property list that has the hashes (also base64 encoded) for each entity that is associated with the data.
The -entitiesByName method on NSManagedObjectModel is useful for dumping the entities and hashes that exist within a specific model.