Chapter    5

Preparing for Change: Migrations and Versioning

By the end of Chapter 4 you had mastered a great deal of the Core Data architecture and functionality by building a fully functioning, albeit somewhat simple, Core Data application. You’ve now got enough Core Data chops to build a solid app, send it to your testers, and then on to the App Store.

But what happens if you change your data model and send a new version of your application out to testers who already have the previous version? Consider the SuperDB app. Let’s say you decide to add a new attribute to the Hero entity; make one of the existing, currently optional attributes required; and then add a new entity. Can you just send the program out to your users or will this cause problems with their data?

As things stand right now, if you make changes to your data model, the existing data sitting in the user’s persistent store on their iPhone will be unusable in the new version of your application. Your application will crash on launch. If you launch the new version from Xcode, you will see a big, scary error message like the following:

2012-07-17 17:33:56.641 SuperDB[11233:c07] Unresolved error Error Domain = NSCocoaErrorDomain Code = 134100 "The operation couldn't be completed. (Cocoa error 134100.)" UserInfo = 0x80a3b30 {metadata = {
    NSPersistenceFrameworkVersion = 409;
    NSStoreModelVersionHashes =     {
        Hero = <0fe30005 4578f63c 124e2af7 3798fb56 7a194f27 f9281223 bd265ee3 d985d2fc>;
    };
    NSStoreModelVersionHashesVersion = 3;
    NSStoreModelVersionIdentifiers =     (
        ""
    );
    NSStoreType = SQLite;
    NSStoreUUID = "719284D9-793C-48A7-8F3E-C633CD4F0402";
    "_NSAutoVacuumLevel" = 2;
}, reason = The model used to open the store is incompatible with the one used to create the store}, {
    metadata =     {
        NSPersistenceFrameworkVersion = 409;
        NSStoreModelVersionHashes =         {
            Hero = <0fe30005 4578f63c 124e2af7 3798fb56 7a194f27 f9281223 bd265ee3 d985d2fc>;
        };
        NSStoreModelVersionHashesVersion = 3;
        NSStoreModelVersionIdentifiers =         (
            ""
        );
        NSStoreType = SQLite;
        NSStoreUUID = "719284D9-793C-48A7-8F3E-C633CD4F0402";
        "_NSAutoVacuumLevel" = 2;
    };
    reason = "The model used to open the store is incompatible with the one used to create the store";
}

If this happens in development, it’s not usually a big deal. If nobody else has a copy of your app and you don’t have any irreplaceable data stored in it, you can just select “Reset Content and Settings” from the iPhone Simulator menu in the simulator or uninstall the application from your iPhone using Xcode’s Organizer window, and Core Data will create a new persistent store based on the revised data model the next time you install and run your application.

If, however, you have given the application to others, they will be stuck with an unusable application on their iPhone unless they uninstall and re-install the application, thereby losing all of their existing data.

As you probably imagine, this is not something that makes for particularly happy customers. In this chapter, we’re going to show you how to version your data model. Then we’ll talk about Apple’s mechanism for converting data between different data model versions, which are called migrations. We’ll talk about the difference between the two types of migrations: lightweight migrations and standard migrations. Then you will set up the SuperDB Xcode project to use lightweight migrations so that the changes you make in the next few chapters won’t cause problems for your (admittedly non-existent) users.

At the end of this chapter, the SuperDB application will be all set up and ready for new development, including changes to your data model, without having to worry about your users losing their data when you ship a new version.

About Data Models

When you create a new Xcode project using a template that supports Core Data, you are provided with a single data model in the form of an .xcdatamodel file in your project. In Chapter 2, you saw how this file was loaded into an instance of NSManagedObjectModel at runtime in the application delegate’s managedObjectModel method. In order to understand versioning and migrations, it’s important to look a little deeper under the hood to see what’s going on.

Data Models Are Compiled

The .xcdatamodel class in your project does not get copied into your application’s bundle the way other resources do. The data model file contains a lot of information that your application doesn’t need. For example, it contains information about the layout of the objects in Xcode’s model editor’s diagram view (Figure 5-1), which is only there to make your life easier. Your application doesn’t care about how those rounded rectangles are laid out, so there’s no reason to include that information inside your application bundle.

9781430238072_Fig05-01.jpg

Figure 5-1.  Certain information, such as that the rounded rectangle representing the Hero entity is in the upper-left corner and that the disclosure triangles next to Attributes and Relationships are expanded, is stored in the .xcdatamodel file but not in the .mom file

Instead, your .xcdatamodel files get compiled into a new type of file with an extension of .mom, which stands for managed object model (sorry, Mom). This is a much more compact binary file that contains just the information that your application needs. This .mom file is what is actually loaded to create instances of NSManagedObjectModel.

Data Models Can Have Multiple Versions

You most likely understand what versioning means in a general sense. When a company releases a new version of a piece of software with new features, it typically has a new number or designation. For example, you are working on a specific version of Xcode (for us, it’s 4.5) and a specific version of Mac OS X (for us it’s 10.8, also known as Mountain Lion).

These are called marketing version identifiers or numbers, as they are primarily intended to tell customers the difference between various released versions of the software. Marketing versions are incremented when a new version of the program is released to customers.

There are other, finer-grained forms of versioning used by developers, however. If you’ve ever used a concurrent versioning system such as cvs, svn, or git, you’re probably aware of how this all works. Versioning software keeps track of the changes over time to all of the individual source code and resource files that make up your project (among other things).

Note  We’re not going to discuss regular version control, but it’s a good thing to know about if you’re a developer. Fortunately, there are a lot of resources on the Web for learning how to install and use different version-control software packages. A good place to start is the Wikipedia page on version control at http://en.wikipedia.org/wiki/Revision_control.

Xcode integrates with several version-control software packages, but it also has some built-in version-control mechanisms, including one that’s intended for use with Core Data data models. Creating new versions of your data models is the key to keeping your users happy. Every time you release a version of your application to the public, you should create a new version of your data model. This will create a new copy so that the old version can be kept around to help the system figure out how to update the data from a persistent store made with one version to a newer version.

Creating a New Data Model Version

Single-click SuperDB.xcdatamodeld in Xcode. Now click the Editor menu and select “Add Model Version.” You will be asked to name this new version. The default values Xcode presents to you (Figure 5-2) are fine.  Just click Finish.

9781430238072_Fig05-02.jpg

Figure 5-2.  Naming the new data model version

You just added a new version of your data model. Once you click Finish, the SuperDB.xcdatamodeld file will gain a disclosure triangle next to it. It will be opened to reveal two different versions of your data model (Figure 5-3).

9781430238072_Fig05-03.jpg

Figure 5-3.  A versioned data model contains the current version, marked with a green checkmark on its icon, along with every previous version

The icon for one of the versions will have a green checkmark on it. This indicates the current version (in your case, SuperDB.xcdatamodel), which is the one that your application will use. By default, when you create a new version, you actually create a copy of the original. However, the new version keeps the same file name as the original, whereas the name of the copy is appended with an incrementally larger number. This file represents what your data model looked like when you created the new version and it should be left untouched.

The fact that the higher number is the older file might seem a little weird but, as more versions accumulate, the numbering will make more sense. The next time you create a new version, the old version will be named SuperDB 3.xcdatamodel, and so on. The numbering makes sense for all the non-current versions, since each version will have a number one higher than the previous one. By keeping the name of the current model the same, it’s easy to tell which is the one you can change.

The Current Data Model Version

In Figure 5-3, SuperDB.xcdatamodel is the current version of the data model and SuperDB 2.xcdatamodel is the previous version. You can now safely make changes to the current version, knowing that a copy of the previous version exists, frozen in time, which will give you the ability to migrate your users’ data from the old version to the next version when you release it.

You can change which version is the current version. To do this, select SuperDB.xcdatamodeld, then open the File Inspector in the Utility pane (Figure 5-4). You should see a section named Version Core Data Model. Find a drop-down box labeled Current. Here you can select the data model you want to make current. You won’t do this often, but you might do it if you need to revert to an older version of the application for some reason. You can use migrations to go back to an older version or move to a new version.

9781430238072_Fig05-04.jpg

Figure 5-4.  The Core Data (Directory) File Inspector

Data Model Version Identifiers

Although you can assign version identifiers like 1.1 or Version A to data models by selecting the specific data model version in the Navigation pane and bringing up the File Inspector (Figure 5-5), this identifier is purely for your own use and is completely ignored by Core Data.

9781430238072_Fig05-05.jpg

Figure 5-5.  The File Identity pane for a data model will allow you to set a version identifier

Instead, Core Data performs a mathematical calculation called a hash on each entity in your data model file. The hash values are stored in your persistent store. When Core Data opens your persistent store, Core Data uses these hash values to ensure that the version of your data stored in the store is compatible with the current data model.

Since Core Data does its version validation using the stored hash values, you don’t need to worry about incrementing version numbers for versioning to work. Core Data will just know which version a persistent store was created for by looking at the stored hash value and comparing it to the hash calculated on the current version of the data model.

Migrations

As you saw at the beginning of the chapter, when Core Data detects that the persistent store in use is incompatible with the current data model, it throws an exception. The solution is to provide a migration to tell Core Data how to move data from the old persistent store to a new one that matches the current data model.

Lightweight vs. Standard

There are two different types of migrations supported by Core Data. The first, called a lightweight migration, is only available in the case of relatively straightforward modifications to your data model. If you add or remove an attribute from an entity, or add or delete an entity from the data model, for example, Core Data is perfectly capable of figuring out how to migrate the existing data into the new model. In the case of a new attribute, it simply creates storage for that attribute, but doesn’t populate it with data for the existing managed objects. In a lightweight migration, Core Data actually analyzes the two data models and creates the migration for you.

If you make a change that’s not straightforward and thus can’t be resolved by the lightweight migration mechanism, then you have to use a standard migration. A standard migration involves creating a mapping model and possibly writing some code to tell Core Data how to move the data from the old persistent store to the new one.

Standard Migrations

The changes you will be making to the SuperDB application in this book are all pretty straightforward, and an in-depth discussion of standard migrations is beyond the scope of this book. Apple has documented the process fairly thoroughly in the developer documentation, though, so you can read more about standard migrations at http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/CoreDataVersioning/Articles/Introduction.html.

Setting Up Your App to Use Lightweight Migrations

On the other hand, you will be using lightweight migrations a lot through the rest of the book. In every remaining Core Data chapter, you will create a new version of your data model and let lightweight migrations handle moving the data. However, lightweight migrations are not turned on by default, so you need to make some changes to your application delegate to enable them.

Edit AppDelegate.m and find the persistentStoreCoordinator method.  Replace this line

if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
    configuration:nil
    URL:storeURL
    options:nil
    error:&error]) {

with these lines

NSDictionary *options = @{NSMigratePersistentStoresAutomaticallyOption:@YES,
              NSInferMappingModelAutomaticallyOption:@YES};
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
    configuration:nil
    URL:storeURL
    options:options
    error:&error]) {

The way to turn on lightweight migrations is to pass a dictionary into the options argument when you call the addPersistentStoreWithType:configuration:URL:options:error: method to add your newly created persistent store to the persistent store coordinator. In that dictionary, you use two system-defined constants, NSMigratePersistentStoresAutomaticallyOption and NSInferMappingModelAutomaticallyOption, as keys in the dictionary, and store an NSNumber under both of those keys that holds an Objective-C BOOL value of YES. By passing a dictionary with these two values in when you add the persistent store to the persistent store coordinator, you indicate to Core Data that you want it to attempt to automatically create migrations if it detects a change in the data model version, and if it’s able to create the migrations, to automatically use those migrations to migrate the data to a new persistent store based on the current data model.

And that’s it. With these changes, you are ready to start making changes to your data model without fear. (Well, maybe not completely without fear.) By using lightweight migrations, you limit the complexity of the changes you’re able to make. For example, you won’t be able to split an entity up into two different entities or move attributes from one entity to another, but the majority of changes you’ll need to make outside of major refactoring can be handled by lightweight migrations. Plus, once you set your project up the way you’ve done in this chapter, that functionality is basically free.

Time to Migrate On

After a couple of long, conceptually difficult chapters, taking a break to set up your project to use migrations gave you a nice breather, but don’t underestimate the importance of migrations. The people who use your applications are trusting you to take a certain amount of care with their data. Putting some effort into making sure that your changes don’t cause major problems for your users is important.

Any time you put out a new release of your application with a new data model version, make sure you test the migration thoroughly. This is true regardless of whether you’re using the lightweight migrations you set up in this chapter or the heavier-duty standard migrations.

Migrations, especially lightweight migrations, are relatively easy to use, but they hold the potential for causing your users significant inconvenience, so don’t get lulled into a false sense of security by how easy they are to use. Test every migration thoroughly with as much realistic data as you can.

And with that warning out of the way, let’s continue adding functionality to the SuperDB application. Up next? Custom managed objects for fun and profit.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.223.195.97