iOS Reference Library Apple Developer
Search

Lightweight Migration

This article describes the “lightweight migration” feature you can use to perform automatic data migration for simple model changes.

Overview

If you just make simple changes to your model (such as adding a new attribute to an entity), on Mac OS X v10.6 and later and on iOS, Core Data can perform automatic data migration, referred to as lightweight migration. Lightweight migration is fundamentally the same as ordinary migration, except that instead of you providing a mapping model (as described in “Mapping Overview”), Core Data infers one from differences between the source and destination managed object models.

Lightweight migration is especially convenient during early stages of application development, when you may be changing your managed object model frequently, but you don’t want to have to keep regenerating test data. You can migrate existing data without having to create a custom mapping model for every model version used to create a store that would need to be migrated.

A further advantage of using lightweight migration—beyond the fact that you don’t need to create the mapping model yourself—is that if you use an inferred model and you use the SQLite store, then Core Data can perform the migration in situ (solely by issuing SQL statements). This can represent a significant performance benefit as Core Data doesn’t have to load any of your data. Because of this, you are encouraged to use inferred migration where possible, even if the mapping model you might create yourself would be trivial.

Requirements

To perform a lightweight migration, Core Data needs to be able to find the source and destination managed object models itself at runtime. (Core Data searches the bundles returned by NSBundle’s allBundles and allFrameworks methods.) It must then analyze the schema changes to persistent entities and properties and generate an inferred mapping model. For Core Data to be able to do this, the changes must fit an obvious migration pattern, for example:

If you rename an entity or property, you can set the renaming identifier in the destination model to the name of the corresponding property or entity in the source model. You typically set the renaming identifier using the Xcode Data Modeling tool, (for either an NSEntityDescription or an NSPropertyDescription object). In Xcode, the renaming identifier is in the User Info pane of the Detail Pane, below the version hash modifier (see The Browser View in Xcode Tools for Core Data). You can also set the identifier at runtime using setRenamingIdentifier:. For example, to handle

you would include the following code after loading the destination data model, and before attempting to open a store file:

NSEntityDescription *automobile = [[destinationModel entitiesByName] objectForKey:@"Automobile"];
[automobile setRenamingIdentifier:@"Car"];
NSPropertyDescription *paintColor = [[automobile attributesByName] objectForKey:@"paintColor"];
[paintColor setRenamingIdentifier:@"color"];

iPhone Development on Mac OS X v10.5: The data modeling tool in Xcode for Leopard does not provide a renaming identifier text field for entities and properties. In-place and lightweight migration are still supported, but you must assign the renaming identifiers in code.

Automatic Lightweight Migration

To request automatic lightweight migration, you set appropriate flags in the options dictionary you pass in addPersistentStoreWithType:configuration:URL:options:error:. You need to set values corresponding to both the NSMigratePersistentStoresAutomaticallyOption and the NSInferMappingModelAutomaticallyOption keys to YES:

NSError *error;
NSURL *storeURL = <#The URL of a persistent store#>;
NSPersistentStoreCoordinator *psc = <#The coordinator#>;
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
    [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
    [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
 
if (![psc addPersistentStoreWithType:<#Store type#>
    configuration:<#Configuration or nil#> URL:storeURL
    options:options error:&error]) {
    // Handle the error.
}

If you want to determine in advance whether Core Data can infer the mapping between the source and destination models without actually doing the work of migration, you can use NSMappingModel’s inferredMappingModelForSourceModel:destinationModel:error: method. This returns the inferred model if Core Data is able to create it, otherwise nil.

Manual Migration

To perform automatic migration, Core Data has to be able to find the source and destination managed object models itself at runtime (see “Requirements”). If you need to put your models in the locations not checked by automatic discovery, then you need to generate the inferred model and initiate the migration yourself. The following code sample illustrates how you can do this. The code assumes that you have implemented two methods—model1 and model2—that return the source and destination managed object models respectively.

- (BOOL)migrateStore:(NSURL *)storeURL toVersionTwoStore:(NSURL *)dstStoreURL {
 
    NSError *error;
    NSMappingModel *mappingModel = [NSMappingModel inferredMappingModelForSourceModel:[self model1]
        destinationModel:[self model2] error:&error];
    if (error) {
        NSString *message = [NSString stringWithFormat:@"Inferring failed %@ [%@]",
            [error description], ([error userInfo] ? [[error userInfo] description] : @"no user info")];
        NSLog(@"Failure message: %@", message);
 
        return NO;
    }
 
    NSValue *classValue = [[NSPersistentStoreCoordinator registeredStoreTypes] objectForKey:NSSQLiteStoreType];
    Class sqliteStoreClass = (Class)[classValue pointerValue];
    Class sqliteStoreMigrationManagerClass = [sqliteStoreClass migrationManagerClass];
 
    NSMigrationManager *manager = [[sqliteStoreMigrationManagerClass alloc]
                                      initWithSourceModel:[self model1] destinationModel:[self model2]];
 
    if (![manager migrateStoreFromURL:storeURL type:NSSQLiteStoreType
                  options:nil withMappingModel:mappingModel toDestinationURL:dstStoreURL
                  destinationType:NSSQLiteStoreType destinationOptions:nil error:&error]) {
 
        NSString *message = [NSString stringWithFormat:@"Migration failed %@ [%@]",
            [error description], ([error userInfo] ? [[error userInfo] description] : @"no user info")];
        NSLog(@"Failure message: %@", message);
 
        return NO;
    }
    return YES;
}



Last updated: 2010-02-24

Did this document help you? Yes It's good, but... Not helpful...