Expenses: A Core Data Tutorial Part 4, Versioning

Basic Data Versioning

This time we are going to be working with some basic data migration. In OS X 10.4 & 10.5 data migration was a bit tricky (mostly because the Apple docs weren’t very clear about a method you had to add to make all the pieces work). OS X 10.6 has made things a bit easier. We will cover the differences when we get there.

As before here is a link to the project as we left it at the end of our last edition.

Expenses3.zip

Creating the new model

To start with we need to create a new data model file. Select the current data model, then select Design -> Data Model -> Add Model Version.

Add Model Version

This will create a copy of the data model file named “MyDocument 2.xcdatamodel.” if you look you will also notice that in the Groups & Files list there has been a change where the original file was, it now has a disclosure triangle and a new extension “.xcdatamodeld” this is the directory that all the versions of the model file will go. The original file also has a green check mark on it’s icon now to indicate that it is the current version of the data model. You can either edit the original or duplicate file, both will work. I prefer to edit the duplicate file so I can keep track of things. Select the new model file then Design -> Data Model ->Set Current Version. For this project just add an attribute named “notes” to the Expenses entity with a type of string.

Adding Notes Attribute

Save the new model file. To see why I always edit the duplicate file select the data model and create another new version, you will see that it is number 3 and is a copy of version 2. All the versions of the model file are sorted in order so it is very easy to see which one is the newest one. Of course for a commercial app you may want to name data models based on the app version so you know what version has what data model for support and the such. You can delete version 3 of the model file as we won’t be using it.

GUI Changes

In order to actually use our new notes attribute we need to make some changes to the GUI. First, open MyDocument.xib and select the Expenses tab. lengthen the window, tab view, and box to allow for the addition of an NSTextView. Drag over an NSTextView sizing it as needed. Click on the text view as needed to actually get the NSTextView selected. In the inspector turn off rich text and then bind the Value to: ExpenseView Array Controller.selection.notes. You could just save and close at this point, but first look at the Categories tab. Lots of dead space down there, perhaps we should do something about that. For a simple fix just lengthen the two table views, moving things down as needed, to take up the extra space. Now, save and close.

The New GUI

Turning on Migration

The thing that the Apple documentation was always a little lacking on in OS X 10.4 & 10.5 was actually turning on the migration, It took me forever the first time I tried to do any migration to actually make things work before I found this little tidbit. We have to override  – (BOOL) configurePersistentStoreCoordinatorForURL: (NSURL *)url ofType: (NSString *) fileType modelConfiguration: (NSString *) configuration storeOptions: (NSDictionary *) storeOptions error: (NSError **)error in MyDocument.m to let the system know it should migrate the data. Here is where things diverge a bit for OS X 10.4 & 10.5 versus 10.6. For the older versions we use the following code along with a mapping model, which we will cover in a moment.:

– (BOOL) configurePersistentStoreCoordinatorForURL: (NSURL *)url ofType: (NSString *) fileType modelConfiguration: (NSString *) configuration storeOptions: (NSDictionary *) storeOptions error: (NSError **)error

{

NSMutableDictionary *options = nil;

if (storeOptions != nil)

{

options = [storeOptions mutableCopy];

}

else

{

options = [[NSMutableDictionary alloc] init];

}

[options setObject:[NSNumber numberWithBool:YES] forKey:NSMigratePersistentStoresAutomaticallyOption];

BOOL result = [super configurePersistentStoreCoordinatorForURL:url ofType:fileType modelConfiguration:configuration storeOptions:options error:error];

[options release], options = nil;

return result;

}

With a migration this simple, just adding an attribute to an entity, in Snow Leopard we use code that looks just like above with just the addition of a second key in the dictionary set to yes that will save us making a mapping model.:

– (BOOL) configurePersistentStoreCoordinatorForURL: (NSURL *)url ofType: (NSString *) fileType modelConfiguration: (NSString *) configuration storeOptions: (NSDictionary *) storeOptions error: (NSError **)error

{

NSMutableDictionary *options = nil;

if (storeOptions != nil)

{

options = [storeOptions mutableCopy];

}

else

{

options = [[NSMutableDictionary alloc] init];

}

[options setObject:[NSNumber numberWithBool:YES] forKey:NSMigratePersistentStoresAutomaticallyOption];

// Add this line for simple migrations in Snow Leopard

[options setObject:[NSNumber numberWithBool:YES] forKey: NSInferMappingModelAutomaticallyOption];

BOOL result = [super configurePersistentStoreCoordinatorForURL:url ofType:fileType modelConfiguration:configuration storeOptions:options error:error];

[options release], options = nil;

return result;

}

Mapping Model

A mapping model tells the system how to get from one version of a data model to the other. For basic migrations we only need a mapping model for OS X 10.4 & 10.5, for more advanced migrations we need a mapping model under OS X 10.6 as well. For this project the mapping model will be very basic. We start by creating a new mapping model file (File-> New File…), this changed from OS X 10.5 to 10.6 so both views are shown: (Note that if you aren’t presented with the mapping model option cancel, select the data model and try again.)

Mapping Model in 10.5

OS X 10.5

Mapping Model in 10.6

OS X 10.6

I named mine “ver1to2” but the name is fairly unimportant. After you name the file you will be asked for the source and destination files. Select the original model for the source and the new model for the destination.

Source and destination for the mapping Model

Since all we did was add one attribute the default mapping model will suffice. Xcode will look at both models and see that there is only one additional attribute and map all the pre-existing entities and attributes to the proper destinations. If you wanted to fill the notes attribute with some sort of migrated from old store message you could do it in this file.

If you hadn’t overridden – (BOOL) configurePersistentStoreCoordinatorForURL: (NSURL *)url ofType: (NSString *) fileType modelConfiguration: (NSString *) configuration storeOptions: (NSDictionary *) storeOptions error: (NSError **)error you would get something like this (I got this a lot the first time I tried to do migration):

Of course you have overridden that method so you won’t have that problem.

That’s all folks

That brings us to the end of our time together today. I’m pondering what we should do in the next edition, but hopefully it will be something interesting. If for some reason something isn’t working for you at this point here is a zip file of the project at this point, along with a sample file:

Expenses4.zip

As always, I look forward to your comments, questions, complaints, suggestions, etc.


Advertisements

One Response to “Expenses: A Core Data Tutorial Part 4, Versioning”

  1. Clay Says:

    Hey, thanks for the Core Data tutorials. You may want to check out WordPress.com’s code highlighting:
    http://en.support.wordpress.com/code/posting-source-code/

    I hope you continue to write tutorials!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: