Julien's Blog : Migrating a local store to iCloud in iOS 6.1

As discussed in a previous post there’s a bug on iOS 6.0 when migrating a persistent store from iCloud storage to local storage. iOS 6.1 seems to fix this, but introduces a new bug when migrating in the opposite direction, from local to iCloud. Apple notes this in the release notes:

Known Issues
Moving data from a local sandbox to iCloud using migratePersistentStore causes a crash. Instead, manually migrate the data store by iterating over the objects in the local data store file.

I outlined the basic procedure to perform such a manual migration previously, but I recommend you use Drew McCormack’s MCPersistentStoreMigrator class found in his iCloudCoreDataTester github repository1. This class uses your model’s NSEntityDescriptions to implement generic methods to perform a manual migration.

Here’s an example on how you can use MCPersistentStoreMigrator:

/* Load the iCloud store, optionally migrate fallback store to iCloud */
- (BOOL)loadiCloudStoreMigrate:(BOOL)migrate error:(NSError * __autoreleasing *)error
{
    BOOL success = YES;
    NSError *localError = nil;
    NSFileManager *fm = [NSFileManager defaultManager];
    NSURL *iCloudDataURL = [fm URLForUbiquityContainerIdentifier:nil];
    iCloudDataURL = [iCloudDataURL URLByAppendingPathComponent:kTransactionLogsDirectory];
    NSDictionary *options = @{
        NSPersistentStoreUbiquitousContentNameKey : kiCloudContentName,
        NSPersistentStoreUbiquitousContentURLKey : iCloudDataURL,
        NSMigratePersistentStoresAutomaticallyOption: @YES,
        NSInferMappingModelAutomaticallyOption: @YES
    };

    if (migrate) {
        NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
        MCPersistentStoreMigrator *migrator = [[MCPersistentStoreMigrator alloc] initWithManagedObjectModel:model
                                                                                             sourceStoreURL:self.fallbackStoreURL
                                                                                        destinationStoreURL:self.iCloudStoreURL];
        migrator.destinationStoreOptions = options;
        [migrator beginMigration];
        NSError *migrateError;
        /* call migrateEntityWithName:batchSize:save:error for each of your entities
        if (![migrator migrateEntityWithName:@"SomeEntity" batchSize:500 save:YES error:&localError]) {
            NSLog(@"Error migrating SomeEntity entities to iCloud: %@", migrateError);
            success = NO;
        }
        [migrator endMigration];        
    }

    if (success) {
        [self.persistentStoreCoordinator lock];
        self.iCloudStore = [self.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
                                                                         configuration:nil
                                                                                   URL:self.iCloudStoreURL
                                                                               options:options
                                                                                 error:&localError];
        [self.persistentStoreCoordinator unlock];
        success = (self.iCloudStore != nil);
    }

    if (!success) {
        if (localError && (error != NULL)) {
            *error = localError;
        }        
    }
    return success;
}

  1. You should also take a look at his series of blog posts about iCloud and Core Data from May/June 2012

You can contact me on Twitter or at julien@caffeine.lu

Previous posts

  1. Creating a Tilt-shift Effect with CoreImage
  2. Migrating an iCloud CoreData Store
  3. Problems with Core Data iCloud Storage
  4. Getting Location Data for an UIImage
  5. Show/Hide Invisible Chars in Vim