Preventing a Coredata Crash for Upgrading Users

Preventing a CoreData crash for upgrading users

If your app is crashing in the Simulator when upgrading, your users will have crashes too.

To avoid this, you need to make sure you follow these steps:

  1. Make sure you do NOT change the original version of your data model in any way.
  2. In Xcode, select your xcdatamodel file, then from the menu choose Editor > Add Model Version...
  3. Xcode will suggest a new version name, based on the current model. Make a mental note of the new version name, then click Finish.
  4. Select the xcdatamodel file again, go to File inspector and under Model Version, select the new version name to make this your current version.
  5. In Project Navigator, select the new version of the xcdatamodel. Add your attribute.

It's important to follow these steps in this order. If you add your attribute before creating the new model or making it your current version, you will have crashes.

EDIT: This will only work if you enable lightweight migrations. This is a code snippet of how to do this:

let options = [NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true]

do {
//coordinator is an NSPersistentStoreCoordinator
try coordinator!.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: options
} catch var error as NSError {

// handle error however you want here...
abort()
}

Update iOS Core Data Version to Avoid a Crash

If you are using any version control system, you can retrieve the old model and do it the right way, follow this documentation to do so.

In order to stop the users app from crashing, you can erase the old sqlite file and generate a new one using the updated model.

You can do something similar to this code. Notice that this will erase the sqlite file every time your app is not able to open it

__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];

if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error])
{
if(error)
{
//Erase old sqlite
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:&error];

//Make new persistent store for future saves
if (![self.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {
// do something with the error
}
}
}

Crash on change to Core Data after migration

You can't keep changing the same model version and expect Core Data to keep up. The migration options you're using only apply when the data doesn't match the current model version but does match some older version included in your app. With automatic lightweight migration, Core Data figures out how to update your persistent store to use the new model. If you change the same version of the model, you may prevent Core Data from matching the persistent store file to any model version, and then you get this exception.

You mention that you made some changes and it worked. Not every change affects the version hash for the model. A Core Data model is uniquely identified by the version hashes of its entities as returned by the [NSEntityDescription versionHash]. That in turn depends on the result of the value of versionHash on NSRelationshipDescription, NSAttributeDescription, and NSPropertyDescription. Some minor changes don't affect any version hashes. In general changes don't affect the version hash unless they affect the formatting of data in the underlying SQLite file.

Adding a new attribute will change the versionHash of the NSEntityDescription, which is why you're having this problem. If you were still migrating data from your original model, it would still work. It's only a problem because you have data that used a model version that doesn't match anything in your app right now.

To fix, do one of the following:

  • Create a new third model version with this change and migrate to that, or
  • Remove your existing data and migrate version 1--> 2 again, or
  • Undo this most recent change so that your data continues to match your latest model version.

CoreData prevent crashing on update

The simplest way is to create a new version of the model :

http://www.raywenderlich.com/27657/how-to-perform-a-lightweight-core-data-migration

After you've make that in place you can also overide the peristentStoreCoordinator method to destroy the database and recreate it in case of extreme bad situation (user lost all his data, but the app start) by adding the code below (objc) instead of just abort it will destroy the database, and create an empty one. It can be usefull in dev, that should be a safeguard in prod but your code should never go there.

//delete the store
[[NSFileManager defaultManager] removeItemAtPath:storePath error:nil];
// recreate the store
if (![persistentStoreCoordinator_ addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}


Related Topics



Leave a reply



Submit