Core data update model with app update? - iphone

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;
}

Related

More iCloud Core Data synching woes

So, it finally happened. The worst case scenario for any independent iPhone developer occurred. Several users are reporting complete data loss after upgrading my app. iCloud Core Data sync is not working. My users are using this app partially to run their businesses. This is a truly catastrophic failure.
The only iCloud related thing I changed was to add the key-value store to iCloud. The core data code remained exactly the same, same model version (no migration) etc.
In my tests everything worked beautifully! But to my dismay, users reported that their data was not there anymore when they opened the updated app.
What could be the reason for this?
The persistent store URL (an ubiquitous URL) should not have changed.
Merge conflicts are also unlikely, as this problem should have arisen before the update.
Some interference with the new ubiquitous key-value store perhaps?
(I have pretty much ruled this out.)
Below please find the code for my managed object model and persistent store. Let me know if you need anything else to assess the problem.
- (NSManagedObjectContext *)managedObjectContext {
if (managedObjectContext_ != nil) {
return managedObjectContext_;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
managedObjectContext_ = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[managedObjectContext_ performBlockAndWait:^{
[managedObjectContext_ setPersistentStoreCoordinator:coordinator];
if (useICloud) {
[managedObjectContext_ setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(mergeiCloud:)
name:NSPersistentStoreDidImportUbiquitousContentChangesNotification
object:coordinator];
}
}];
}
return managedObjectContext_;
}
and
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator_ != nil) {
return persistentStoreCoordinator_;
}
NSMutableDictionary *options = [NSMutableDictionary dictionary];
NSURL *storeURL = [[self applicationDocumentsDirectory]
URLByAppendingPathComponent:#"SalesCalls.sqlite"];
[options setObject:[NSNumber numberWithBool:YES]
forKey:NSMigratePersistentStoresAutomaticallyOption];
[options setObject:[NSNumber numberWithBool:YES]
forKey:NSInferMappingModelAutomaticallyOption];
if (useICloud) { // this is an internal flag set to YES
NSURL *iCloudURL = [[NSFileManager defaultManager]
URLForUbiquityContainerIdentifier:nil];
if (nil!=iCloudURL) {
NSString *cloudData = [[iCloudURL path]
stringByAppendingPathComponent:#"data"];
iCloudURL = [NSURL fileURLWithPath:cloudData];
[options setObject:#"at.topofmind.salesplus.store"
forKey:NSPersistentStoreUbiquitousContentNameKey];
[options setObject:iCloudURL
forKey:NSPersistentStoreUbiquitousContentURLKey];
NSURL *nosyncDir = [iCloudURL
URLByAppendingPathComponent:#"data.nosync"];
[[NSFileManager defaultManager] createDirectoryAtURL:nosyncDir
withIntermediateDirectories:YES
attributes:nil error:nil];
storeURL = [nosyncDir
URLByAppendingPathComponent:#"SalesCalls.sqlite"];
}
}
NSError *error = nil;
persistentStoreCoordinator_ = [[NSPersistentStoreCoordinator alloc]
initWithManagedObjectModel:[self managedObjectModel]];
if (![persistentStoreCoordinator_
addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil URL:storeURL options:options error:&error])
{
NSLog(#"Cannot create persistent store coordinator, %#, %#",
error, [error userInfo]);
abort();
}
return persistentStoreCoordinator_;
}
Comments, opinions, wild guesses etc. (and of course solutions!) are all welcome.
UPDATE:
One of my customers lost all his data and after reinstalling and resetting everything (rebooting device, etc.) he could not sync with iCloud any more. The symbol simply does not show up in the Settings.
I was facing something similar with few uses ( by your description I assumed you have way more volume than I do, that's might the reason for several cases )
In my case Apple seemed to have removed without earlier notice the 20gb free space in the icloud. I noticed that the 2 uses who are power data usage users had lost of new data from my app (which was to deal with historical stock pricing ) and the others ones we just fine downloading the same data size.
I followed up with those 2, helping them to clean stuff up to let more space in icloud and voila, after downloading the data again it worked fine.

Core Data Error: Persistent store coordinator is not getting the File. It is the Phase appDelegate.Below the bolded phase has error.

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

Core Data - Migration question?

I am trying to do a migration
I have 2 versions of model
1.xcdatamodel
2.xcdatamodel
I created a mapping model from version 1 to 2
1to2.xcmappingmodel
The problem is that it can't find the migration model that I created so mappingModel always gets nil.
Is there anything I have to do to specify what mappingModel it ahould use?
target = [[NSManagedObjectModel alloc] initWithContentsOfURL:[NSURL fileURLWithPath:modelPath]];
//target and source are initialized correctly
mappingModel = [NSMappingModel mappingModelFromBundles:nil forSourceModel:source destinationModel:target];
It might be that you changed one of your models after creating the mapping model.
Even if a change does not seem relevant it will change the hash value of the model which is used for finding the appropriate mapping model.
At least I've been bitten by this just now :-)
If you've already created a mapping model from 1.xcdatamodel to 2.xcdatamodel, and properly configured it, then you should be able to do something like this: [Note: the key is specifying NSMigratePersistentStoresAutomaticallyOption]
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (persistentStoreCoordinator)
return persistentStoreCoordinator;
NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"MyStore.sqlite"]];
NSError *error = nil;
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, nil];
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeUrl
options:options
error:&error])
{
// Handle error
NSLog(#"Error adding persistent store...%#", error);
// Handle the error.
NSLog(#"Failed to save to data store: %#", [error localizedDescription]);
NSArray* detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
if(detailedErrors != nil && [detailedErrors count] > 0)
{
for(NSError* detailedError in detailedErrors)
{
NSLog(#" DetailedError: %#", [detailedError userInfo]);
}
}
else
{
NSLog(#" %#", [error userInfo]);
}
}
else
{
DLog(#"Persistent store added without incident, apparently.");
}
return persistentStoreCoordinator;
}
To answer the original question, your code looks alright but I'm not why you passed nil as the bundles parameter. The documentation doesn't say one can. So:
NSArray *theBundles = [NSArray arrayWithObject:[NSBundle mainBundle]];
mappingModel = [NSMappingModel mappingModelFromBundles:theBundles
forSourceModel:source
destinationModel:target];
If you pass nil as bundle parameter, it will take [NSBundle mainBundle].
[To response to question of Elise van Looij]

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.

Core Data migration failing with error: Failed to save new store after first pass of migration

In the past I had already implemented successfully automatic migration from version 1 of my data model to version 2. Now, using SDK 3.1.3, migrating from version 2 to version 3 fails with the following error:
Unresolved error Error Domain=NSCocoaErrorDomain Code=134110 UserInfo=0x5363360 "Operation could not be completed. (Cocoa error 134110.)", {
NSUnderlyingError = Error Domain=NSCocoaErrorDomain Code=256 UserInfo=0x53622b0 "Operation could not be completed. (Cocoa error 256.)";
reason = "Failed to save new store after first pass of migration.";
}
I have tried automatic migration using NSMigratePersistentStoresAutomaticallyOption and NSInferMappingModelAutomaticallyOption and also migration using only NSMigratePersistentStoresAutomaticallyOption, providing a mapping model from v2 to v3.
I see the above error logged, and no object is available in the application. However, if I quit the application and reopen it, everything is in place and working.
The Core Data methods I am using are the following ones
- (NSManagedObjectModel *)managedObjectModel {
if (managedObjectModel != nil) {
return managedObjectModel;
}
NSString *path = [[NSBundle mainBundle] pathForResource:#"MYAPP" ofType:#"momd"];
NSURL *momURL = [NSURL fileURLWithPath:path];
managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:momURL];
return managedObjectModel;
}
- (NSManagedObjectContext *) managedObjectContext {
if (managedObjectContext != nil) {
return managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
managedObjectContext = [[NSManagedObjectContext alloc] init];
[managedObjectContext setPersistentStoreCoordinator: coordinator];
}
return managedObjectContext;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator != nil) {
return persistentStoreCoordinator;
}
NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"MYAPP.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]) {
// Handle error
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
return persistentStoreCoordinator;
}
In the simulator, I see that this generates a MYAPP~.sqlite files and a MYAPP.sqlite file. I tried to remove the MYAPP~.sqlite file, but
BOOL oldExists = [[NSFileManager defaultManager] fileExistsAtPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"MYAPP~.sqlite"]];
always returns NO. Any clue? Am I doing something wrong?
Thank you in advance.
I ran into this as well and after reading as much Apple docs and web postings as I could find there didn't seem to be an answer. In my case the manual migration was working as well but when I went to open up a new coordinator it would give the same error you had. I finally decided to go back to my last working version of the data model and do a series of small changes/versions and see where it broke the auto-migration capabilities to drill further into it and it turned out it didn't. Now I can add entities, attributes and relationships without issue and they auto-migrate. Any chance you deleted an interim version of the datamodel?
For what it's worth, the Magical Record Core Data utility package includes this hack:
[coordinator MR_addAutoMigratingSqliteStoreNamed:storeFileName];
//HACK: lame solution to fix automigration error "Migration failed after first pass"
if ([[coordinator persistentStores] count] == 0)
{
[coordinator performSelector:#selector(MR_addAutoMigratingSqliteStoreNamed:) withObject:storeFileName afterDelay:0.5];
}
You might try something similar. I have been unable to find an explanation of what the problem is, though, or why just retrying it would work.