I have an iphone app that uses Core Data to do storage. I have successfully deployed it, and now I'm working on the second version. I've run into a problem with the data model that will require a few very simple data transformations at the time that the persistent store gets upgraded, so I can't just use the default inferred mapping model.
My object model is stored in an .xcdatamodeld bundle, with versions 1.0 and 1.1 next to each other. Version 1.1 is set as the active version. Everything works fine when I use the default migration behavior and set NSInferMappingModelAutomaticallyOption to YES. My sqlite storage gets upgraded from the 1.0 version of the model, and everything is good except for, of course, the few transformations I need done.
As an additional experimental step, I added a new Mapping Model to the core data model bundle, and have made no changes to what xcode generated. When I run my app (with an older version of the data store), I get the following
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Object's persistent store is not reachable from this NSManagedObjectContext's coordinator'
What am I doing wrong? Here's my code for to get the managed object model and the persistent store coordinator.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"gti_store.sqlite"]];
NSError *error;
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(#"Eror creating persistent store coodinator - %#", [error localizedDescription]);
}
return _persistentStoreCoordinator;
}
- (NSManagedObjectModel *)managedObjectModel {
if(_managedObjectModel == nil) {
_managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];
NSDictionary *entities = [_managedObjectModel entitiesByName];
//add a sort descriptor to the 'Foo' fetched property so that it can have an ordering - you can't add these from the graphical core data modeler
NSEntityDescription *entity = [entities objectForKey:#"Foo"];
NSFetchedPropertyDescription *fetchedProp = [[entity propertiesByName] objectForKey:#"orderedBar"];
NSSortDescriptor* sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"index" ascending:YES] autorelease];
NSArray* sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
[[fetchedProp fetchRequest] setSortDescriptors:sortDescriptors];
}
return _managedObjectModel;
}
I haven't thought this out very carefully, it's just an observation as I was having the same problem, and I too have found very few references to this error on the web.
In my case the problem was that I had setup one of my application's objects to observe
NSManagedObjectContextObjectsDidChangeNotification like so
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(observeContextSave:)
name:NSManagedObjectContextDidSaveNotification
object:nil];
- (void) observeContextSave:(NSNotification*) notification {
[[self managedObjectContext] mergeChangesFromContextDidSaveNotification:notification];}
Once I moved this code so that is was executed after the migration, the error went away.
Anyway. I'm sure your circumstances are different. But it may help to think about what observations you have setup on notifications from your managedObjectContext.
Update: Having thought about this a bit more, I guess it happens because multiple persistent stores are created during migration, which means that NSManagedObjectContextDidSaveNotification will be sent from a context with a different persistent store to the context that is sent mergeChangesFromContextDidSaveNotification.
First, turn off the automatic migration if you are having a mapping model, fair chance they are colliding. Once you have done that, confirm that the error is gone.
I had similar issue when persistent store initialization was performed from secondary thread. After I forced initialization in primary thread the problem has gone. Weird.
Seems you already got it fixed, but worth to mention. I had this error too, the reason was I had multiple MOCs being notified of mergeChangesFromContextDidSaveNotification while they had different persistent stores (or different schemas). They didn't know how to handle changes that didn't belong to their stores.
Related
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.
In My Application
I have one sqlite file CoreDataBountyHunter.sqlite
I am using the Core Data Object Model to connect this sqlite file.
As it is the Basic application I have got the error before fetching the data or actual code part. I have got the error like Persistent store coordinator can not connect to file or Model.
1) visits the view will appear part
NSManagedObjectContext *moc=appDelegate.managedObjectContext. Part
from that it goes to the
2) Some code which shows File is Exists
3) then it goes to the Below code of persistent store coordinator from which it calls the Function of MANAGED OBJECT MODEL which is present at next code here...
After returning back to persistent coordinator in if condition it has got error and aborting so please help
-
//1--------
(NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (__persistentStoreCoordinator != nil)
{
return __persistentStoreCoordinator;
}
NSURL *storeURL=[[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"CoreDataBountyHunter.sqlite"];
NSLog(#"%#",storeURL);
NSError *error = nil;
**__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error])**
{
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.
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
**abort();** I have error at this place
} ``
return __persistentStoreCoordinator;
}
enter code here
- (NSManagedObjectModel *)managedObjectModel
{
if (__managedObjectModel != nil)
{
return __managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"CoreDataBountyHunter" withExtension:#"momd"];
__managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return __managedObjectModel;
}
//-------------------------------------------------------------------------------------
Actually it is a total mess with how Core data is working with different paths to read Write data so please explain also. Thanks Why I am getting the error at this place.
So, After visiting the MANAGED OBJECT MODEL CODE IT GOES TO THE PERSISTENT COORDINATOR SO AT THIS PLACE IT SHOULD HAVE SOME MESS...
Any time If you change the .xcdamodel file then your application will crash surely.
Can you try to this code in core data:
- (NSManagedObjectModel *)managedObjectModel {
if (managedObjectModel != nil) {
return managedObjectModel;
}
managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];
return managedObjectModel;
}
No need to shout...
What happens when you use the more usual
+ (NSManagedObjectModel *)mergedModelFromBundles:(NSArray *)bundles
as in
- (NSManagedObjectModel *)managedObjectModel
{
if (!__managedObjectModel)
{
__managedObjectModel = [[NSManagedObjectModel mergedModelFromBundle:nil] retain];
}
return __managedObjectModel;
}
Unless you have multiple models it's slightly cleaner
A suggestion:
Whenever you want to make changes in the core data file then don' do it directly in the same file or version of core data. Best practice is to follow the Modal versions for core data. It will prevent the crashes and also It will be very helpful in case when your app is already in production.
For modal version, refer this link. For more description please research on Google.
https://developer.apple.com/library/ios/recipes/xcode_help-core_data_modeling_tool/Articles/creating_new_version.html
I have been working with Core Data in an iPad app and I can successfully save and fetch data inside the app. However when completely closing the application, fully, quit, take it out of multitasking, and that data disappears.
So does Core Data in anyway keep this data anywhere when the app is closed? Or do I need to look somewhere else?
EDIT: This is in the app delegate didFinishLaunchingWithOptions: [[[UIApplication sharedApplication] delegate] managedObjectContext]; and then I have this: context_ = [(prototypeAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext]; in the UIView subclass.
This is the NSPersistentStoreCoordinator code premade in the app delegate:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator_ != nil) {
return persistentStoreCoordinator_;
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"prototype.sqlite"];
NSError *error = nil;
persistentStoreCoordinator_ = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![persistentStoreCoordinator_ addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil 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.
If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application's resources directory instead of a writeable directory.
If you encounter schema incompatibility errors during development, you can reduce their frequency by:
* Simply deleting the existing store:
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]
* Performing automatic lightweight migration by passing the following dictionary as the options parameter:
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return persistentStoreCoordinator_;
}
So far I am using this to fetch data:
NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
NSEntityDescription *testEntity = [NSEntityDescription entityForName:#"DatedText" inManagedObjectContext:context_];
[fetch setEntity:testEntity];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"dateSaved == %#", datePicker.date];
[fetch setPredicate:pred];
NSError *fetchError = nil;
NSArray *fetchedObjs = [context_ executeFetchRequest:fetch error:&fetchError];
if (fetchError != nil) {
NSLog(#"fetchError = %#, details = %#",fetchError,fetchError.userInfo);
}
noteTextView.text = [[fetchedObjs objectAtIndex:0] valueForKey:#"savedText"];
And this to save data:
NSManagedObject *newDatedText;
newDatedText = [NSEntityDescription insertNewObjectForEntityForName:#"DatedText" inManagedObjectContext:context_];
[newDatedText setValue:noteTextView.text forKey:#"savedText"];
[newDatedText setValue:datePicker.date forKey:#"dateSaved"];
NSError *saveError = nil;
[context_ save:&saveError];
if (saveError != nil) {
NSLog(#"[%# saveContext] Error saving context: Error = %#, details = %#",[self class], saveError,saveError.userInfo);
}
Do you save the context in the right places? It is a common mistake not to save the context when entering background application state only in willTerminate.
Save the context in the following appdelegate method:
-(void)applicationDidEnterBackground:(UIApplication *)application
You are saving your context directly after inserting the object, this should be sufficient. Check the sqlite file in simulator if it contains any data after saving.
if
noteTextView.text = [[fetchedObjs objectAtIndex:0] valueForKey:#"savedText"];
does not throw an exception, there is an object found in context. Maybe it does not contain the expected value?
Log the returned object from your fetchrequest to console to see if this might be the case
You should post the code that sets up your NSPersistentStoreCoordinator and adds your NSPersistentStore. By any chance are you using NSInMemoryStoreType as the type of your store? Because that would result in the behavior you're seeing. Alternately, you could be using a different path to the store each time, which would give you a fresh store each time. In general, your store should be in your Documents folder, and it should be given the same name on every launch. It should also use the NSSQLiteStoreType
I have discovered the problem. It turns out that due to its use of UIDatePicker, at the start of the program it set that date picker to today using:
NSDate *now = [[NSDate alloc] init];
[datePicker setDate:now];
So without using this it works perfectly. So currently I am looking for a solution to this issue, as this line seems to cause the problem.
UIDatePicker Interfering with CoreData
If you add a CoreData after create your project.There is a risk to make mistake in lazy_init your NSManagedObject.
-(NSManagedObjectContext*) managedObjectContext{
if (!_managedObjectContext) {
_managedObjectContext =[self createManageObjectContextWithName:#"name.sqlite"];
}
return _managedObjectContext;}
Here is the right way:
- (NSManagedObjectContext *)managedObjectContext {
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (!coordinator) {
return nil;
}
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
return _managedObjectContext;}
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
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.