Why Does CoreData crash when I add an Attribute? - iphone

Everytime I add a new Attribute to my CodeData object model I have to clear my database file out otherwise I get the following error:
2010-11-13 15:26:44.580 MyApp[67066:207] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '+entityForName: could not locate an NSManagedObjectModel for entity name 'myApp''
There must be a way of being able to add extra fields without losing the whole database.
What do I need to do to retain my data?

there is a way, and this way is called automatic lightweight migration. It needs a codechange and an extra step when changing your object model.
For the code you have to add two options to the method where you initialize your persistent store coordinator. something like this:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator_ != nil) {
return persistentStoreCoordinator_;
}
NSString *storePath = [AppDelegate_Shared coredataDatabasePath];
NSURL *storeURL = [NSURL fileURLWithPath:storePath];
// important part starts here
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]) {
// and ends here
LogError(#"Unresolved error %#, %#", error, [error userInfo]);
// Do something
}
return persistentStoreCoordinator_;
}
Now if you want to change your model, you have to create a model version before you do any changes.
Select your datamodel, and go into the main menu Design -> Data Model -> Add Model Version. Your "old" model will be renamed and you make your changes in the current model, the one with the green mark.
All the old models are kept and will be put into your application, so your app can perform the 'automatic lightweight migration' and upgrade the existing database to your new model.

In addition to #Matthias Bauch's answer
for Xcode 12.3
Choose from the main menu Editor -> Add Model Version
To add mark the New Model as the current model with a green checkmark
Follow the below image

Related

Core data update model with app update?

everyone who work with Core Data know the message "the model used to open the store is incompatible with the one used to create the store".
Then I have to delete my app from simulator, and rebuilding it again.
My question is if I submit an app v 1.0, then add some entities to core data in v 1.1, does this mean that the users of 1.0 who updated to 1.1 will have their data cleared up?
You will need to create a new model version for your model, and migrate the database. You can do a lightweight migration if your model changes are within the required changes. If not, you will need to tell core data how to migrate your data. Check the migration documentation: http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/CoreDataVersioning/Articles/Introduction.html
In your case it sounds like a simple extension to your old data model. If you just really add some new entities or even new classes then the so called leightweight migration is the right way to go for you.
Actually in this case you almost do not have anything to do, but create your second model IN ADDITION to your original model. It is important, that you have BOTH model, then the app will just load your 1st version without any problems as well as the new version.
Don't forget to mark your new model as the new one!
Try to be careful when creating the new model, since deleting a model is a real hassle.
Your code will look very similar to this:
-(NSManagedObjectContext *)managedObjectContext {
if (managedObjectContext != nil) {
return managedObjectContext;
}
NSPersistentStoreCoordinator *lC = [self persistentStoreCoordinator];
if (lC != nil) {
managedObjectContext =[[NSManagedObjectContext alloc] init];
[managedObjectContext setPersistentStoreCoordinator: lC];
}
return managedObjectContext;
}
- (NSPersistentStoreCoordinator *) persistentStoreCoordinator {
if (persistentStoreCoordinator != nil) {
return persistentStoreCoordinator;
}
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
// Allow inferred migration from the original version of the application.
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"DBName.sqlite"]];
NSError *error = nil;
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl
options:options error:&error]){
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
return persistentStoreCoordinator;
}
- (NSManagedObjectModel *) managedObjectModel {
if (managedObjectModel != nil) {
return managedObjectModel;
}
managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
return managedObjectModel;
}

iOS 5 - Coredata Sqlite DB losing data after killing app

I'm using coredata with a sqlite DB to persist data in my app. However, each time I kill my app I lose any data that was saved in the DB. I'm pretty sure its because the .sqlite file for my DB is just being replaced by a fresh one each time my app starts, but I can't seem to find any code that will just use the existing one thats there.
It would be great if anyone could point me towards some code that could handle this for me.
Cheers
B
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (__persistentStoreCoordinator != nil)
{
return __persistentStoreCoordinator;
}
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"FlickrCoreData.sqlite"];
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;
}
Changes to a managed object context in core data are not saved at the time you make the changes for optimization purposes. This way you can make a bunch of changes to your context and then persist all the changes at once. So if you are killing your app before it has a chance to autosave you will then lose all your data. I'm guessing this is what you are experiencing here.
In any case, try explicitly making a call to save your data before closing your app. This should solve your problem.
For example, assuming you have a variable that holds your managed object context called context you can save your context by making the following call somewhere in your code before closing the app:
[context save:&error] or simply [context save:nil]
Have you tried place [self saveContext] in appDelegate function applicationWillTerminate:. You should save the context before terminate the application.

Core Data : Post migration, additional migration code

I wish to migrate from my version1 data model to version2, but once the migration is complete I wish to perform some custom migration code. How will I know if/when the migration occurs? Is there a migrationHasCompleed delegate method or notification?
For interests sake: The custom migration code I wish to perform resizes png's in the database.
For reference, you can also test in advance whether a migration is necessary, which would probably be cleaner.
NSError *error;
NSDictionary *sourceMetadata = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:NSSQLiteStoreType
URL:storeURL
error:&error];
NSManagedObjectModel *destinationModel = [persistentStoreCoordinator managedObjectModel];
BOOL migrationRequired = ![destinationModel isConfiguration:nil compatibleWithStoreMetadata:sourceMetadata];
// Now add persistent store with auto migration, and do the custom processing after
Well, you could run this code right after the migration happen, on your persistent store coordinator setup:
+ (NSPersistentStoreCoordinator *)persistentStoreCoordinatorForStore:(NSString *)store {
NSURL *storeUrl = [NSURL fileURLWithPath:[[[self class] applicationDocumentsDirectory] stringByAppendingPathComponent:store]];
NSError *error = nil;
NSPersistentStoreCoordinator *persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[[self class] managedObjectModel]];
// Check for model changes without trying to update
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error]) {
// Set the automatic update options for the current model
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption,
nil];
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]) {
NSLog(#"Error opening the database. Deleting the file and trying again.");
//delete the sqlite file and try again
[[NSFileManager defaultManager] removeItemAtPath:storeUrl.path error:nil];
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error])
{
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
Typical reasons for an error here include:
* The persistent store is not accessible
* The schema for the persistent store is incompatible with current managed object model
Check the error message to determine what the actual problem was.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
} else { // The model was updates successfuly
// Implement here your custom migration code
}
}
return [persistentStoreCoordinator autorelease];
}
Cheers,
vfn

Core Data Lightweight Migration - Cant Merge Models

I need to add some attributes to my core data model and I am having a terrible time getting lightweight migration to work!
I keep getting the error "Cant merge models with two different entities named blah".
Here's what I've done ...
Added this code to my app delegate.
(NSPersistentStoreCoordinator*)persistentStoreCoordinator {
//blah blah
NSDictionary* options =
[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
//blah blah
return _persistentStoreCoordinator;
}
Clicked on my data model, went to Design > Data Model > Add Model Version.
Made my changes to the one with the LOWEST number, basically adding a few attributes.
Deleted all the managed files produced from my previous model, sent them to trash, then created new ones from the new model.
Cleaned all targets.
Build and go.
ERROR.
Please please help. I've tried the above in numerous different ways, and loads of other stuff, each time going back to a clean copy of my project and starting again, and nothing has got me past this error.
Thanks!
Well, once again, another 6 hours of my life completely wasted because Apple are a bunch of ... well, I'll stop there.
Anyway, thanks to this lovely person: http://linkroller.com/fullpage/ad/13754/?http://iphonedevelopment.blogspot.com/2009/09/core-data-migration-problems.html I was able to solve the problem.
You follow the steps I already followed, then you need to find the following method:
- (NSManagedObjectModel *)managedObjectModel {
if (managedObjectModel != nil) {
return managedObjectModel;
}
managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];
return managedObjectModel;
}
and change it to:
- (NSManagedObjectModel *)managedObjectModel {
if (managedObjectModel != nil) {
return managedObjectModel;
}
NSString *path = [[NSBundle mainBundle] pathForResource:#"Foo" ofType:#"momd"];
NSURL *momURL = [NSURL fileURLWithPath:path];
managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:momURL];
return managedObjectModel;
}
where foo is the name of you xcdatamodeld file.
AAAAAARGH.
I had fixed the core data migration
pls follwing this steps
Go AppDelegate.m write function
-(NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (__persistentStoreCoordinator != nil)
{
return __persistentStoreCoordinator;
}
NSString *databaseFilePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"sampleiOS.sqlite"];
NSURL *storeUrl = [NSURL fileURLWithPath: databaseFilePath];
NSDictionary *_option = [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:_option error:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
return __persistentStoreCoordinator;
}
then select example.xcdatamodeld file
click the Editor menu on the top => add model version => create the new model version "example 2.xcdatamodel" then click finish button.
now show two core data model version one is source "example.xcdatamodel" another one is destination "example 2.xcdatamodel".
now add an attribute or entity in your new version datamodel "example 2.xcdatamodel". then click the group data model "example.xcdatamodeld". After that set current version to be newly created data model "example 2.xcdatamodel".
How to set current Version
select show utilities => show inspector => versioned core data model . then set current version.

CoreData could not fulfill a fault when adding new attribute

I am receiving a "CoreData could not fulfill a fault for ..." error message when trying to access a new attribute in a new data model. If I work with new data I'm ok, but when I attempt to read existing data I get the error. Do I need to handle the entity differently myself if the attribute isn't in my original data? I was under the impression that Core Data could handle this for me. My new attribute is marked as optional with a default value.
I have created a new .xcdatamodel (and set it to be the current version) and updated my NSPersistentStoreCoordinator initialization to take advantage of the lightweight migration as follows:
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();
}
Any help is appreciated.
UPDATE:
After more digging I've updated my managedObjectModel to:
- (NSManagedObjectModel *)managedObjectModel {
if (managedObjectModel != nil) {
return managedObjectModel;
}
//managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];
NSString *path = [[NSBundle mainBundle] pathForResource:#"< MyModel >" ofType:#"momd"];
NSURL *momURL = [NSURL fileURLWithPath:path];
managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:momURL];
return managedObjectModel;
}
This still hasn't resolved my problems. I've clean and rebuilt, but still no love.
How are you constructing your NSManagedObjectModel? If you are passing it a specific file that might be causing your issue as you may be loading the older, original mom file that is lingering around your project. Ideally you should be now loading the momd bundle or just loading all compiled models from your bundle using:
NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
Which, if you receive an error, indicates that you need to clean your project to get rid of stale compiled models.
Update
Since it is not an old model issue, we move onto the next possibility. That the version is not set correctly in the plist. To check this, use finder or terminal and look inside of the momd bundle and open the plist therein. Check that to confirm that the new model is indeed set as the current version.
Assuming that does not work, next run your app in the simulator and have it save immediately upon creation of the MOC. After that, open the sqlite3 file using the command line tool and check the schema to see if it has updated to the new structure.
Assuming that is set correctly, are you using custom NSManagedObject subclasses?
Turns out that there was no problem with the versioning. I had some rather (too) complex logic which removed my object from the model and then I later tried to access it.
+1 to Marcus for the additional debugging pointers, they will no doubt come in handy at some point.