Lightweight migrations in Core Data

In the previous article, I hope you got the basic understanding of Core Data. Lightweight migrations in Core Data solves the biggest problem of app crashing.

In the previous article, we created Data model named with an Entity Student which contains two attributes name and course of type String. Now, try to add one more attribute age and run the Xcode project. You might encounter an error and the result is a crash on launch, rendering the application unusable.

  NSStoreType = SQLite;
    NSStoreUUID = "020DA737-E193-4BBA-9E16-2DC0A2D97FDE";
    "_NSAutoVacuumLevel" = 2;
}, reason=The model used to open the store is incompatible with the one used to create the store}}, NSLocalizedDescription=Failed to initialize the application's saved data, NSLocalizedFailureReason=There was an error creating or loading the application's saved data.}, {
    NSLocalizedDescription = "Failed to initialize the application's saved data";
    NSLocalizedFailureReason = "There was an error creating or loading the application's saved data.";
    NSUnderlyingError = "Error Domain=NSCocoaErrorDomain Code=134100 \"(null)\" UserInfo={metadata={\n    NSPersistenceFrameworkVersion = 849;\n    NSStoreModelVersionHashes =     {\n        Student = <1e123eb3 1f3d13ca c12b582d f685d535 237a1869 30ed4fbd f07eb951 e146902d>;\n    };\n    NSStoreModelVersionHashesVersion = 3;\n    NSStoreModelVersionIdentifiers =     (\n        \"\"\n    );\n    NSStoreType = SQLite;\n    NSStoreUUID = \"020DA737-E193-4BBA-9E16-2DC0A2D97FDE\";\n    \"_NSAutoVacuumLevel\" = 2;\n}, reason=The model used to open the store is incompatible with the one used to create the store}";

Whenever we modify the data model of a Core Data application, the persistent store becomes incompatible with the data model.

Lightweight Migrations

Lightweight migrations are ideal for expanding the data model, adding attributes and entities. Changing attribute types is restricted using lightweight migrations. If you plan to modify relationships or change attribute types, heavy migration is the only answer for you. Lightweight migration is strongly recommended. In this article, we will focus only on Lightweight Migrations.

Lightweight Migration is based on Versioning of data model. You have to Add a new model version which enables to safely modify the data model.

Student App

I hope you are done with the basic biolerplate code from our previous article of Core Data. So, moving forward, I add one more attribute in Student Entity say, age of string type.

Now try to run the app, app crashes on abort() function.

This is because when we launched the application for the first time, Core Data created an SQLite database based on the data model. However, because we changed the data model by adding an attribute to the Student entity, age, Core Data no longer understands how it should store Student records in the SQLite database.

In other words, the modified data model is no longer compatible with the persistent store it created earlier.

Change code in Appdelegate.m file

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
    // The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it.
    if (_persistentStoreCoordinator !=   nil) {
        return _persistentStoreCoordinator;
    // Create the coordinator and store
    _persistentStoreCoordinator  =   [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    NSURL *storeURL  =   [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"CoreDataDemo.sqlite"];
     //Performs Lightweight Migration
    NSDictionary *options = @{
                              NSMigratePersistentStoresAutomaticallyOption : @YES,
                              NSInferMappingModelAutomaticallyOption : @YES
    NSError *error  =   nil;
    NSString *failureReason  =   @"There was an error creating or loading the application's saved data.";
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil
                                                             URL:storeURL options:options error:&error])
        // Report any error we got.
        NSMutableDictionary *dict  =   [NSMutableDictionary dictionary];
        dict[NSLocalizedDescriptionKey]  =   @"Failed to initialize the application's saved data";
        dict[NSLocalizedFailureReasonErrorKey]  =   failureReason;
        dict[NSUnderlyingErrorKey]  =   error;
        error  =   [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
        // Replace this 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.
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    return _persistentStoreCoordinator;

These two lines provide the complete magic of Lightweight migrations. The first key, NSMigratePersistentStoresAutomaticallyOption, tells Core Data that we'd like it to attempt to migrate the persistent store for us. The second key, NSInferMappingModelAutomaticallyOption, instructs Core Data to infer the mapping model for the migration.

Add a new model version(Final Step)

Now, in File Inspector update the Model Version. Select CoreDataModel2.

Hope, big problem of Lightweight Migration is solved for your app. So, this was all about how Lightweight Migration is done. In next article, we will learn how to fetch data from core data using NSFetchResultsController