Core Data migration problem: "Persistent store migration failed, missing source managed object model."
Asked Answered
L

6

11

The Background

  • A Cocoa Non Document Core Data project with two Managed Object Models.
  • Model 1 stays the same. Model 2 has changed, so I want to migrate the store.
  • I've created a new version by Design > Data Model > Add Model Version in Xcode.
  • The difference between versions is a single relationship that's been changed from to a one to many.
  • I've made my changes to the model, then saved.
  • I've made a new Mapping Model that has the old model as a source and new model as a destination.
  • I've ensured all Mapping Models and Data Models and are being compiled and all are copied to the Resource folder of my app bundle.
  • I've switched on migrations by passing in a dictionary with the NSMigratePersistentStoresAutomaticallyOption key as [NSNumber numberWithBool:YES] when adding the Persistent Store.
  • 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:

The Problem

No matter what I do to migrate, I get the error message:

"Persistent store migration failed, missing source managed object model."

What I've Tried

  • I clean after every single build.
  • I've tried various combinations of having only the model I'm migrating to in Resources, being compiled, or both.
  • Since the error message implies it can't find the source model for my migration, I've tried having every version of the model in both the Resources folder and being compiled.
  • I've made sure I'm not making a really basic error by switching back to the original version of my data model. The app runs fine.
  • I've deleted the Mapping Model and the new version of the model, cleaned, then recreated both.
  • I've tried making a different change in the new model - deleting an entity instead.

I'm at my wits end.

I can't help but think I've made a huge mistake somewhere that I'm not seeing. Any ideas?

Lubric answered 22/3, 2010 at 12:19 Comment(0)
C
16

Two possibilities:

  1. Your source model in your app does not match the actual store on disk.
  2. 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:

Crawly answered 22/3, 2010 at 12:37 Comment(7)
Great answer, Marcus. Thanks. I'll double check the hashes. I'm pretty sure it's not option 1, but I'll check nonetheless. Xcode must have a good reason for showing this error message.Lubric
@Marcus How do we do Update Source Model in Xcode 5?Weirick
@Marcus I didn't get the sentence you wrote: "you can tell it to update from source from the design menu in Xcode" - where? how?Leavening
Answer updated. Xcode has changed over the last 6 years.Crawly
@Marcus: thanks for maintaining answer. Unfortunately is the screenshot no longer valid. Furthermore, do I need to keep both file source file versions, for the data model and for the "implementing" classes?Maebashi
@WizardofKneup Image re-uploaded. You need to keep both model files but you do not need to keep the implementing classes.Crawly
This answer finally helped me narrow down my long search with the incredibly useful debug option key for migration, available in that provided link. com.apple.CoreData.MigrationDebug 1 helped me see that the migration was being applied twice, and allowed me to adjust the code in a better way.Ceiba
C
6

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.

Climactic answered 1/4, 2010 at 5:1 Comment(1)
ohhorob: "This doesn't seem right. Why merge the models? You want to use model 2, migrating your store from model 1." I believe his two models are for two different sets of entities/attributes/relations. The two models don't have the same entities -- they are not related. Like model 1 for Employees/Departments and model 2 for Products/Customers.Cubit
T
4

When I got this error, I had updated my core data models but not cleared the app instance from my test phone. This meant the models saved to core data on the phone did not match what I was trying to use in the code.

I removed the app from the phone and re-built/ran successfully.

Threecornered answered 6/2, 2019 at 17:50 Comment(1)
Fixed it for me. Thank you!Postmark
W
3

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.

Wilmot answered 2/6, 2011 at 20:56 Comment(2)
hi xyzzycoder! i also had simliar issue. can u tell me how u migrated core data in given case (third-party Framework was writing data into an app's database). thanksWhangee
@MohitNigam This was a while ago, but I ended up opening up the sqlite datastore directly (prior to bringing up the core data stack), dropping the third-party's tables, updating the metadata entries to remove references to the third-party tables, closing the sqlite file, and then spooling Core Data up (and it was then able to do the automatic migration). I actually approached the Core Data team at WWDC about this, and they did not have a better idea.Wilmot
K
1

I had a similar problem. I have used +modelByMergeingModels:, but I did not use Mapping Model. However merging models does not work with lightweight data migration.

From the Apple Docs:

To perform automatic lightweight migration, Core Data needs to be able to find the source and destination managed object models itself at runtime.

If you use +modelByMergeingModels: than that is used for the destination model. However Core Data will not be able to find source model. Source model has been created using +modelByMergeingModels: in older version of the application and Core Data does try to merge models to find out the source model.

What I ended up doing is that I have (manually) created a new merged .xcdatamodeld by editing the XML files of the models, added it into the project, removed the separate .xcdatamodelds from Compile Sources and instead of using +modelByMergeingModels: use NSManagedObjectModel's -initWithContentsOfURL: with the URL of the new merged model. I'll probably create a script that will automatically merge the models in the future.

Katharyn answered 20/11, 2014 at 17:52 Comment(0)
C
1

If you have this issue for a Mac Catalyst application then you need to delete the stored data which can be found in ~/Library/Containers/name-of-app

Coddle answered 18/8, 2021 at 8:15 Comment(1)
This fixed it for me. The only catch was that the container's name was hashed (e.g. 7F8E9252-CFFA-446C-A0D7-D00B97DB18DE), so I found it by looking for the most recent container, then deleting it.Izanami

© 2022 - 2024 — McMap. All rights reserved.