Core Data migration "can't merge models with two different entities named 'foo'" - iphone

I know this is a frequently asked question, however none of the solutions that I have found seem to work for me.
This is my situation:
I have one data model for my application, and I wanted to add versioning to it. So in XCode, I did Design -> Data Model -> Add Model Version. I also updated my delegate's persistentStoreCoordinator method to look like this:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator != nil) {
return persistentStoreCoordinator;
}
NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory]
stringByAppendingPathComponent: #"foo.sqlite"]];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption,
nil];
NSError *error = nil;
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if(![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]) {
/*Error for store creation should be handled in here*/
}
return persistentStoreCoordinator;
}
Just to make sure everything was still working, I did a clean all, build, and tested it in the simulator. Everything worked so far.
Next I chose the new version data model, set it to be the current version using XCode, and added one extra attribute to an entity. I then did a clean all, build. And now whenever I start the application it crashes with this error: 'Can't merge models with two different entities named 'foo''.
What am I doing wrong? I have tried making sure that no data model is added to the target, adding just the current version data model to the target, and both. Every time I test I make sure to clean all.
Can anyone shed some light as to why it does not work for me?
EDIT:
here is my managedObjectModel method:
- (NSManagedObjectModel *)managedObjectModel {
if (managedObjectModel != nil) {
return managedObjectModel;
}
managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];
return managedObjectModel;
}

I already expected this implementation of the managedObjectModel getter.
In your implementation all models within the bundle are merged into one single model. Therefore also all versions within the .momd are merged resulting in duplicate entity definitions.
Change the code to explicitly initialize the model with the apropriate model file and it should work fine.
NSString *modelPath = [[NSBundle mainBundle] pathForResource:#"datamodel" ofType:#"momd"];
NSURL *modelURL = [NSURL fileURLWithPath:modelPath];
managedObjectModel_ = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];

If you're using Version Models on Core Data you always have to initialize to the version of the model that you want to use. On the Application Bundle you will find one file with extension .momd that is the Full Model. Inside of this file you will find a lot of .mom files inside, each .mom file represent one version of your model.
If you run your application and initialize with the .momd file and all versions inside, Core Data will create all versions and later we will have the "duplicated entity" error, Core Data don't known what version use. Now, the only way to fix the problem is delete the app, point your code to the correct .mom file and run again, so Core Data create only one version of the internal database.
Here's one snippet of code that do the job:
NSString *fullModelName = #"myModel.momd"; // The name of the main model.
NSString *modelVersionName = #"myModel1.0.mom"; // Only the name of the version.
NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
NSString *modelPath = [NSString stringWithFormat:#"%#/%#/%#", bundlePath, fullModelName, modelVersionName];
//
NSURL *modelURL = [NSURL fileURLWithPath:modelPath];
managedObjectModel_ = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];

Related

Cannot create an NSPersistentStoreCoordinator with a nil model error

I have a coreData datamodel file which was running perfectly. Due to some special requirements i deleted the old datamodel file and created another datamodel file with exactly same entities. There is no change in entities from the previous dataModel. I have made this a part of a different bundle and referring it from that bundle.
Code for creating the managedObjectModel
if (managedObjectModel_ != nil) {
return managedObjectModel_;
}
NSBundle *newBundle = [NSBundle bundleWithURL:[[NSBundle mainBundle] URLForResource:#"dataBundle" withExtension:#"bundle"]];
NSString *modelPath = [newBundle pathForResource:#"DataHouse" ofType:#"momd"];
NSURL *modelURL = [NSURL fileURLWithPath:modelPath];
managedObjectModel_ = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return managedObjectModel_;
The app is running fine till some time and suddenly (randomly) i get an error saying
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Cannot create an NSPersistentStoreCoordinator with a nil model'
*** First throw call stack:`(0x62e052 0x26a9d0a 0xf6e86d 0x64fd 0x624e 0x381b 0x79c9b 0x65f2d 0x1881e0f 0x1882589 0x186ddfd 0x187c851 0x1827322 0x62fe72 0x160892d 0x1612827 0x1598fa7 0x159aea6 0x163437a 0x16341af 0x602966 0x602407 0x5657c0 0x564db4 0x564ccb 0x2791879 0x279193e 0x17e8a9b 0x28a2 0x2815)`
The code for creating the persistant store coordinator
if (persistentStoreCoordinator_ != nil) {
return persistentStoreCoordinator_;
}
NSURL *storeURL = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"DataHouse.sqlite"]];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
NSError *error = nil;
persistentStoreCoordinator_ = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![persistentStoreCoordinator_ addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return persistentStoreCoordinator_;
This error occurs at a random point but is very much consistent every time i run the app.
I am totally confused and struck at this point... I have seen the same problem in the forum but i guess mine is a special scenario. I am pretty sure that the modelPath variable mentioned in the above code snippet is coming fine every time i print it.
NOTE : the above code snippets are not a part of the AppDelegate class. They are a part of a special class that contains all Coredata methods
First verify that managedObjectModel_ is valid, you can use the debugger or call a method on it like
NSLog(#"%#", [managedObjectModel_ entities]);
to verify that your data model is fine. That call should show an array of all your Entities in the model. Next check that your persistent store path is pointing at the correct location. Try this:
NSLog(#"%#", [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"DataHouse.sqlite"]);
Finally (which I think is what needs to be done because it's happened to me) go to the location on the disk where the .sqlite file is stored:
~/Library/Application Support/iPhone Simulator/5.0/Applications/{your app id}/Documents
and delete the .sqlite file so Core Data will generate it fresh the next time you build.
The actual problem turned out to be a memory issue. I was not using core data objects from AppDelegate and i am creating them in some other classes. But i have not released these objects due to which there was a huge leak and the app got crashed. I released the core data objects and now my app is working perfectly fine...
if your datamodel name is Model.xcdatamodeid. just replace the URLForResource parameter as your datamodel name in AppDelegate.h file.
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"Model"
withExtension:#"momd"];
below code for reference.
(NSManagedObjectModel *)managedObjectModel {
// The managed object model for the application. It is a fatal error
for the application not to be able to find and load its model.
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"Model"
withExtension:#"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}

core data application is Crashing in iphone device

Hello I am testing my core data application in device, but it is crashing in iphone and working fine in simulator.Here is my code..
- (NSManagedObjectContext *)managedObjectContext {
if (managedObjectContext != nil) {
return managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
managedObjectContext = [[NSManagedObjectContext alloc] init];
[managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return managedObjectContext;
}
- (NSManagedObjectModel *)managedObjectModel {
if (managedObjectModel != nil) {
return managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"TouristGuide" withExtension:#"momd"];
managedObjectModel= [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
NSLog(#"%#", modelURL);
return managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator != nil) {
return persistentStoreCoordinator;
}
NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent:#"TouristGuide.sqlite"];
NSFileManager *fileManager = [NSFileManager defaultManager];
// If the expected store doesn't exist, copy the default store.
if (![fileManager fileExistsAtPath:storePath]) {
NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:#"TouristGuide" ofType:#"sqlite"];
if (defaultStorePath) {
[fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
}
}
NSURL *storeUrl = [NSURL fileURLWithPath:storePath];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
NSError *error;
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]) {
// Update to handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
return persistentStoreCoordinator;
}
device log is showing exception in line:
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
The exceptions are cannot create nspersistancecordinator with a nil model and second is cannot find Entity 'City'.
How can I solve this problem?
Crash log
0x352d1de0 -[NSPersistentStoreCoordinator initWithManagedObjectModel:] + 252 10 TouristGuide
0x0000358e -[TouristGuideAppDelegate persistentStoreCoordinator] (TouristGuideAppDelegate.m:176) 11 TouristGuide
0x0000310e -[TouristGuideAppDelegate managedObjectContext] (TouristGuideAppDelegate.m:124) 12 TouristGuide
0x000041f6 -[CityViewController viewDidLoad] (CityViewController.m:43)
Hold down the option key, go to Product menu (Xcode 4) and do "Clean Build Folder"... then let off the option key, and do "Clean"...
Now do Product menu > Analyze and make sure all is kosher.
Lastly go to the left hand panel of the project viewer, and click on the icon that represents your entire project. In the window that comes up in the main panel, under "Targets," click on your app. Now click "Build Phases" and expand "Copy Bundle Resources." Make sure that your TouristGuide.xcdatamodeld file is there. Next, expand "Link Binary with Libraries" and make sure that "CoreData.framework" is in there (if you added Core Data to an existing project that didn't start out as a Core Data project, then chances are, it's not in there). Next, expand "Compile Sources" and, of course, double-check that all your model classes are in there (y'know, the NSManagedObject custom sub-classes).
Speaking of those model classes... now click on your TouristGuide.xcdatamodeld and click on the "Default" configuration. Make sure any classes that say "NSManagedObject" in the "Class" column do NOT have custom classes, and if they do list a custom class, make sure you have the equivalently-named .h and .m files corresponding to those.
10-to-1 it's something to do with your model file itself. Onetime I changed my model file's full path (it should be /Users/You/Documents/Developer/MyApp/MyModel.xcdatamodeld/MyModel.xcdatamodel). Once I had changed it, I couldn't figure out how to change it back, because MyModel.xcdatamodeld is a "package" file, not a folder. Sure, in the finder, you can do "Show Package Contents...", but XCode's dialog box for picking the file's location refused to navigate into the package contents! SO I was unable to ever re-select the .xcdatamodel file that resides within the .xcdatamodeld file! I had to totally recreate my entire model file from scratch, it was a huge pain. Maybe there's a way to do it but I couldn't figure it out.
Also, two last tricks.
First last trick, try this on a backup of your project just in case, but it worked for me when I got stuck: change your code from this:
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"TouristGuide" withExtension:#"momd"];
to this:
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"TouristGuide" withExtension:#"mom"];
...change "momd" to "mom".
Then delete the app from device or simulator, "Clean build folder," "clean," and rebuild and re-run the project. It might give you errors, it might not. After that, then change it back to "momd" and redo all those steps. Changing this will might make XCode delete more stuff than it otherwise would when you do "Clean Build Folder" and reinstall the app. That way it's making a fresh "momd" file when you switch back.
Second last trick:
It also never hurts to #import in your headers, just for good luck. :D
Hope this helps. If someone would have told me this stuff at the beginning it would have saved me a lot of time.
The error is actually caused by the self.managedObjectModel property having a nil value. Look at the method where the managedObjectModel is loaded. That is the source of your problem.
Sometimes, Xcode does not remove previous versions of files from development builds on either the simulator or device. The solution is to manually delete the entire app of device and have Xcode rebuild it from scratch.

core data can't find model for source store - what did my old store look like?

So first, this question helped a lot with getting on the right track toward working core data versioning. So I added a new version for my model, and now I'm trying to get the automatic migration working, but I have a problem. I can't remember what my old version looked like! I'm trying to run the app on my phone, but I've been using the simulator for a while and made a few changes to the schema. The version on the phone is from quite a while ago. So each time I try to modify the old version to what I think is on the phone, but I still get the "can't find model for source store" error. I'm guessing it's because I got the old schema wrong.
Is there any way for me to figure out what the schema looks like on the phone? Barring that, how could I just wipe the sqlite store off the phone so I can start over from version 1?
I was tearing my hair out over the "Can't find model for source store" error for a whole day. Here is an elaboration of learner2010's answer for googlers:
Your sqlite database's model hash MUST match one of the mom or momd created by your xcdatamodel when you build your app. You can see the hashes in the momd's VersionInfo.plist in the built app's bundle. See below for code to find your database's model hash.
So if you change your xcdatamodel instead of creating a new version under Xcode->Editor->Add Model Version... then your model's hash will be different, and addPersistentStoreWithType won't be able to use your old database, which used the old model. That's what causes the "Can't find model for source store" error.
To make matters worse, the sqlite database is stored in something like "/private/var/mobile/Library/Mobile Documents/YOU_APP_ID/Data.nosync/YOUR_DB.sqlite" and this can stick around even if you remove the app from the device and reinstall it! So you will think there's something wrong with your code, when in reality you just have a stale database that needs to be deleted. Usually this is during debugging so there's no real data in it anyway.
So the proper workflow to allow migrations in the future is to make your model, run your app to build the database, and then create NEW VERSIONS of the model anytime you need to make changes. Everything will "just work" if you keep the changes minor. Then, when you're ready to release your app, select the final model and delete the rest. Then delete your database from "/private/var/mobile/Library/Mobile Documents". Then on future releases, include all the models from previous releases along with your newest model (if it's changed) and users will be able to migrate each time.
Here is my code so far. The important line is:
[fileManager removeItemAtPath:iCloudData error:&error];
But it's only to be used during debugging to delete your old database. Here is the production code in AppDelegate.m:
- (NSManagedObjectModel *)managedObjectModel
{
if (__managedObjectModel != nil)
{
return __managedObjectModel;
}
//NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"Model" withExtension:#"momd"];
//__managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
//NSArray *testArray = [[NSBundle mainBundle] URLsForResourcesWithExtension:#"momd"subdirectory:nil];
NSString *path = [[NSBundle mainBundle] pathForResource:#"Model" ofType:#"momd"];
if( !path ) path = [[NSBundle mainBundle] pathForResource:#"Model" ofType:#"mom"];
NSURL *modelURL = [NSURL fileURLWithPath:path];
__managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
//__managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
return __managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if((__persistentStoreCoordinator != nil)) {
return __persistentStoreCoordinator;
}
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
NSPersistentStoreCoordinator *psc = __persistentStoreCoordinator;
// Set up iCloud in another thread:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// ** Note: if you adapt this code for your own use, you MUST change this variable:
NSString *iCloudEnabledAppID = #"RW6RS7HS69.com.zsculpt.soaktest";
// ** Note: if you adapt this code for your own use, you should change this variable:
NSString *dataFileName = #"mydailysoak.sqlite";
// ** Note: For basic usage you shouldn't need to change anything else
NSString *iCloudDataDirectoryName = #"Data.nosync";
NSString *iCloudLogsDirectoryName = #"Logs";
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *localStore = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:dataFileName];
NSURL *iCloud = [fileManager URLForUbiquityContainerIdentifier:nil];
if (iCloud) {
NSLog(#"iCloud is working");
NSURL *iCloudLogsPath = [NSURL fileURLWithPath:[[iCloud path] stringByAppendingPathComponent:iCloudLogsDirectoryName]];
NSLog(#"iCloudEnabledAppID = %#",iCloudEnabledAppID);
NSLog(#"dataFileName = %#", dataFileName);
NSLog(#"iCloudDataDirectoryName = %#", iCloudDataDirectoryName);
NSLog(#"iCloudLogsDirectoryName = %#", iCloudLogsDirectoryName);
NSLog(#"iCloud = %#", iCloud);
NSLog(#"iCloudLogsPath = %#", iCloudLogsPath);
if([fileManager fileExistsAtPath:[[iCloud path] stringByAppendingPathComponent:iCloudDataDirectoryName]] == NO) {
NSError *fileSystemError;
[fileManager createDirectoryAtPath:[[iCloud path] stringByAppendingPathComponent:iCloudDataDirectoryName]
withIntermediateDirectories:YES
attributes:nil
error:&fileSystemError];
if(fileSystemError != nil) {
NSLog(#"Error creating database directory %#", fileSystemError);
}
}
NSString *iCloudData = [[[iCloud path]
stringByAppendingPathComponent:iCloudDataDirectoryName]
stringByAppendingPathComponent:dataFileName];
NSLog(#"iCloudData = %#", iCloudData);
NSMutableDictionary *options = [NSMutableDictionary dictionary];
[options setObject:[NSNumber numberWithBool:YES] forKey:NSMigratePersistentStoresAutomaticallyOption];
[options setObject:[NSNumber numberWithBool:YES] forKey:NSInferMappingModelAutomaticallyOption];
[options setObject:iCloudEnabledAppID forKey:NSPersistentStoreUbiquitousContentNameKey];
[options setObject:iCloudLogsPath forKey:NSPersistentStoreUbiquitousContentURLKey];
[psc lock];
NSError *error;
[psc addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:[NSURL fileURLWithPath:iCloudData]
options:options
error:&error];
if( error )
{
NSLog(#"Error adding persistent store %#, %#", error, [error userInfo]);
// comment in this line while debugging if get "Can't find model for source store" error in addPersistentStoreWithType.
// it means the sqlite database doesn't match the new model and needs to be created from scratch.
// this happens if you change the xcdatamodel instead of creating a new one under Xcode->Editor->Add Model Version...
// CoreData can only automatically migrate if there is a new model version (it can't migrate if the model simply changes, because it can't see the difference between the two models).
// be sure to back up the database if needed, because all data will be lost.
//[fileManager removeItemAtPath:iCloudData error:&error];
/*// this is another way to verify the hashes for the database's model to make sure they match one of the entries in the momd directory's VersionInfo.plist
NSDictionary *sourceMetadata = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:NSSQLiteStoreType URL:[NSURL fileURLWithPath:iCloudData] error:&error];
if( !sourceMetadata )
NSLog(#"sourceMetadata is nil");
else
NSLog(#"sourceMetadata is %#", sourceMetadata);*/
}
[psc unlock];
}
else {
NSLog(#"iCloud is NOT working - using a local store");
NSMutableDictionary *options = [NSMutableDictionary dictionary];
[options setObject:[NSNumber numberWithBool:YES] forKey:NSMigratePersistentStoresAutomaticallyOption];
[options setObject:[NSNumber numberWithBool:YES] forKey:NSInferMappingModelAutomaticallyOption];
[psc lock];
NSError *error;
[psc addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:localStore
options:options
error:nil];
if( error )
NSLog(#"Error adding persistent store %#, %#", error, [error userInfo]);
[psc unlock];
}
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:#"SomethingChanged" object:self userInfo:nil];
});
});
return __persistentStoreCoordinator;
}
The error message means it cannot find the .mom compiled model file for the existing store. Core Data is looking for the exact .mom version that configured the store. The .mom file tells tells Core Data how to map the serialized data in the file into objects. Without that model file, it does not know how to migrate the store to the new model because it doesn't know what data goes with each entity or entity property.
I've only seen this once and IIRC the cause was that the new .mom file had the exact same name and location as the old one. When the app was updated, the old .mom file was overwritten.
Try changing the name of the new model file and see if that helps. If not, we will probably need more detail about what you are doing.
I had the same problem too. I solved it by deleting my old database and building the project again. I am not sure that this is the right way to do. Apparently it is pointing to the wrong database file. In my case, I was creating a simple project using core data and was trying out migration. When I created a new model with an additional column and built my project, there were 2 files - coreData.sqlite and coreData ~.sqlite... So what I feel is that, the wrong database is getting pointed to and hence the error. When I deleted the database and built the project again, it worked perfectly for me.
If you do delete the database, it would be best to save a copy of the database in another location so that you do not lose it.
use core data migrations for updating old database with new schema with losing your data.
https://developer.apple.com/library/ios/documentation/cocoa/conceptual/CoreDataVersioning/Articles/vmLightweightMigration.html

Error Building Core Data Stack in Unit Tests

I am trying to get started with unit testing an app that uses Core Data. In the setUp method of my unit first test, I can get the path to my data model but for some reason cannot convert it to a NSURL.
My setUp method is:
- (void)setUp {
NSBundle *bundle = [NSBundle bundleWithIdentifier:#"com.testcompany.LogicTests"];
STAssertNotNil(bundle, #"Error finding bundle to create Core Data stack.");
NSString *path = [bundle pathForResource:#"DataModel" ofType:#"momd"];
STAssertNotNil(path, #"The path to the resource cannot be nil.");
NSURL *modelURL = [NSURL URLWithString:path];
STAssertNotNil(modelURL, #"The URL to the resource cannot be nil. (tried to use path:%#, modelURL is %#)", path, modelURL);
...
}
The error I'm getting is:
/Users/neall/iPhone Apps/TestApp/UnitLogicTests.m:24:0 "((modelURL) != nil)" should be true. The URL to the resource cannot be nil. (tried to use path:/Users/neall/iPhone Apps/TestApp/build/Debug-iphonesimulator/LogicTests.octest/DataModel.momd, modelURL is (null))
I've checked the filesystem and the directory /Users/neall/iPhone Apps/TestApp/build/Debug-iphonesimulator/LogicTests.octest/DataModel.momd exists.
What am I missing here?
Thanks!
Try using [NSURL fileURLWithPath:path] instead to construct the url
Double check that you are seeing a directory called DataModel.momd at /Users/neall/iPhone Apps/TestApp/build/Debug-iphonesimulator/LogicTests.octest/DataModel.momd.
If you added a xcdatamodel file by the Add New File... command in Xcode, you would only have one file and it would be DataModel.mom (no trailing d). If that's the case, changing the
NSString *path = [bundle pathForResource:#"DataModel" ofType:#"momd"];
to
NSString *path = [bundle pathForResource:#"DataModel" ofType:#"mom"];
will fix your immediate issue.
You want to use the fileURLWithPath: that Claus suggested as well.
If you want to do versioning of your model in the future and you currently have only a .mom file, select your DataModel.xcdatamodel file in XCode and go to Design -> Data Model -> Add Model Version. This will force the creation of the DataModel.momd directory with the DataModel.mom file in it. You can just delete the new version it adds into that directory and your original tests will work.
xcdatamodel should also be added to
Project -> Targets -> "unit test target" -> build phases -> compile sources
After spending several hours stacking in July 2014 this post was one of several that in part led me to the working solution.
We somehow managed to break the surprisingly fragile (and mysterious) mechanism that links the bundle that your source code lives in to the bundle that runs the unit test. Further you might have a misnamed xcdatamodel. See comments for explanations:
-(NSManagedObjectContext *) getManagedObjectContext
{
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
//Replace MyClass with class that is from your data model
//really any of your classes should work
NSBundle * bundle = [NSBundle bundleForClass:[MyClass class]];
//You can uses this line to figure you what your bundle is actually named
//In my case the because my PRODUCT_NAME had spaces in it they was replaced with '-'
//(dashes) and I couldn't divine it from the info.plist and the Build Settings.
NSString * ident =[bundle bundleIdentifier];
//This will show you where your app is actually out building temporary files
//The exact location appears to change every version or to of Xcode so
//this is useful for figuring out what your model is named
NSString * bundlePath =[bundle bundlePath];
//Here replace Name_of_model_without_the_dot_xcdatamodel with the name of your
//xcdatamodel file without an extension
//Some tutorials will have you use AppName.xcdatamodel others will simply name it
//DataModel.xcdatamodel.
//In any event if bothe path and path1 return null then check the
//bundlePath by going to Finder and pressing Command-Shift-G and pasting
//bundlePath into the pop-up. Look around for a mom or momd file thats the name you want!
NSString* path = [bundle
pathForResource:#"Name_of_model_without_the_dot_xcdatamodel"
ofType:#"momd"];
//If the above 'path' and 'path1' is not then you want to use this line instead
NSString* path1 = [bundle
pathForResource:#"Name_of_model_without the_dot_xcdatamodel"
ofType:#"mom"];
//the above path lines are simply so you can trace if you have a mom or a momd file
//replace here appropriately
NSURL *modelURL = [bundle URLForResource:#"Name_of_model_without the_dot_xcdatamodel"
withExtension:#"momd"];
//the rest is boiler plate:
NSManagedObjectModel *mom = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
NSPersistentStoreCoordinator *psc =
[[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
[psc addPersistentStoreWithType:NSInMemoryStoreType
configuration:nil URL:nil options:nil error:nil];
[moc setPersistentStoreCoordinator:psc];
return moc;
}
Here is how you might use the above context:
-(void)testMyStuff
{
NSManagedObjectContext* context=[self getManagedObjectContext];
MyClass *myobj=[NSEntityDescription insertNewObjectForEntityForName:#"MyClass"
inManagedObjectContext:context];
}
One final note you may also have to add your source files and xcmodel under the "Compile Sources" of build phases. This unfortunately changes with almost every version of Xcode. For Xcode 5:

Implementation of "Automatic Lightweight Migration" for Core Data (iPhone)

I would like to make my app able to do an automatic lightweight migration when I add
new attributes to my core data model.
In the guide from Apple this is the only info on the subject I could find:
Automatic Lightweight Migration
To request automatic lightweight
migration, you set appropriate flags
in the options dictionary you pass in
addPersistentStoreWithType:configuration:URL:options:error:.
You need to set values corresponding
to both the
NSMigratePersistentStoresAutomaticallyOption
and the
NSInferMappingModelAutomaticallyOption
keys to YES:
NSError *error;
NSURL *storeURL = <#The URL of a persistent store#>;
NSPersistentStoreCoordinator *psc = <#The coordinator#>;
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
if (![psc addPersistentStoreWithType:<#Store type#>
configuration:<#Configuration or nil#> URL:storeURL
options:options error:&error]) {
// Handle the error.
}
My NSPersistentStoreCoordinator is initialized in this way:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator != nil) {
return persistentStoreCoordinator;
}
NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"FC.sqlite"]];
NSError *error = nil;
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return persistentStoreCoordinator;
}
I am having trouble seeing where and how I should add the Apple code to get the Automatic Lightweight Migration working?
This is what I did to make Automatic Lightweight Migration (Source: http://brainwashinc.wordpress.com/2010/01/18/iphone-coredata-automatic-light-migration/)
1. Set the Persistent Store options for automatic migration in the app delegate.
Change your persistentStoreCoordinator creation to this (replace YOURDB):
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator != nil) {
return persistentStoreCoordinator;
}
NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"YOURDB.sqlite"]];
// handle db upgrade
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
NSError *error = nil;
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]) {
// Handle error
}
return persistentStoreCoordinator;
}
2. Version your Data Model and Edit the new file.
Select your xcdatamodel file
Design -> Data Model -> Add Model Version (expand your xcdatamodeld item)
Select the “2″ (or later) file, Design -> Data Model -> Set Current Version (edit this version)
3. Specify the momd resource in app delegate.
Change your managedObjectModel implementation to this (replace YOURDB)
- (NSManagedObjectModel *)managedObjectModel {
if (managedObjectModel != nil) {
return managedObjectModel;
}
NSString *path = [[NSBundle mainBundle] pathForResource:#"YOURDB" ofType:#"momd"];
NSURL *momURL = [NSURL fileURLWithPath:path];
managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:momURL];
return managedObjectModel;
}
At first, the above solution didn't work for me. The returned managedObjectModel was 0x0.
I think this is because I renamed the file names of the different model files.
If you follow the instructions above to the letter then it all works.
However if you do change model file names then you can select the "current" model file manually:
Lets say your original model file was MYMODEL.xcdatamodel
after doing the add model step above this turns into a directory MY.xcdatamodeld
and below it you have MYMODEL.xcdatamodel and MYMODEL 2.xcdatamodel
rename the new model file to whatever you want, for example lets say you removed the space to MYMODEL2.xcdatamodel and edit its content.
Now in the above code do
NSString *path = [mainBundle pathForResource:#"MYMODEL2" ofType:#"mom" inDirectory:#"MYMODEL.momd"];
I think this adds onto the last answer.
I found the usage of the bundle resource and .sqlite names really confusing at first. Does the bundle resource name change with the version change? Does the .sqlite name change? I've now got my migration working, and learned that the bundle model name refers to the name of the directory/folder in XCode containing all the models, not the name of the model versions within that directory.
When you give a modelResource name to:
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:modelResource withExtension:#"momd"];
NSManagedObjectModel *theManagedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
That modelResource name is the directory/folder for the models in Xcode.
When you do:
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:storeFileName];
NSError *error = nil;
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {
// handle error
}
The storeFileName is the name of your .sqlite file in the Documents folder/directory (this is not in the bundle).
Also, when you migrate from one model version to another model version, by default, the .sqlite file name remains the same.
Oscar, in response to your issue, I found the same thing initially. I would suggest deleting and re-adding the new .xcdatamodeld file to your project, and then rebuilding. Hope that helps!
Swift 3 Solution
1. Set the Persistent Store options for automatic migration in the app delegate.
Change your persistentStoreCoordinator creation to this (replace SingleViewCoreData.sqlite):
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
let coordinator: NSPersistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.appendingPathComponent("SingleViewCoreData.sqlite")
let options = [
NSMigratePersistentStoresAutomaticallyOption : Int(true),
NSInferMappingModelAutomaticallyOption : Int(true)
]
do {
try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: options)
} catch {
print(error)
}
return coordinator
}()
2. Version your Data Model and Edit the new file.
Select your xcdatamodel file Editor>Add Model Version - add a name for your new model