I'm trying to perform an iOS Core Data migration that requires a MappingModel. Core data is not able to use the mapping model for some reason and it falls back to an automatic lightweight migration.
I enabled the MigrationDebug option to get more info and what I see makes no sense. The source and destination hashes of the mapping model are identical, ignoring order, to the source and destination ManagedObjectModels. It seems like the mapping model should be used but the log says "no suitable mapping model found".
Here is the (elided) log:
CoreData: annotation: (migration) will attempt automatic schema migration
CoreData: annotation: (migration) looking for mapping model with
source hashes:
{
TSBaseEntity = <4797118c 50068f2f f544d9a9 4884720b 55ec7e4d 0d4c8f4e 1ee44be3 b06d2edc>;
TSBuyer = <91e837d1 3f348913 eff634d6 6fb9b3a6 747e2390 fbdc4ae6 32cc56d6 7582d4a8>;
...
}
destination hashes: {
TSBaseEntity = <4797118c 50068f2f f544d9a9 4884720b 55ec7e4d 0d4c8f4e 1ee44be3 b06d2edc>;
TSBuyer = <e316a857 8919c4be eef15387 5c67a21b 67d32919 99ead438 1ff93c05 2e065fcc>;
...
}
CoreData: annotation: (migration) checking mapping model at path file://localhost/Users/xandrews/Library/Application%20Support/iPhone%20Simulator/6.1/Applications/0A84951E-21FC-47C0-A1B7-F880ACB672C4/Dev.app/Migrate_0_5_24To_0_5_27.cdm
source hashes:
{(
<4797118c 50068f2f f544d9a9 4884720b 55ec7e4d 0d4c8f4e 1ee44be3 b06d2edc>,
<91e837d1 3f348913 eff634d6 6fb9b3a6 747e2390 fbdc4ae6 32cc56d6 7582d4a8>,
...
)}
destination hashes: {(
<4797118c 50068f2f f544d9a9 4884720b 55ec7e4d 0d4c8f4e 1ee44be3 b06d2edc>,
<e316a857 8919c4be eef15387 5c67a21b 67d32919 99ead438 1ff93c05 2e065fcc>,
...
)}
CoreData: annotation: (migration) no suitable mapping model found
CoreData: annotation: (migration) inferring a mapping model between data models with
source hashes: ...
In your persistentStoreCoordinator method give this line of code
NSDictionary *options=[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],NSMigratePersistentStoresAutomaticallyOption,[NSNumber numberWithBool:YES],NSInferMappingModelAutomaticallyOption, nil];
If this doesnt help then you need to go for user implemented migration. So you will have to create a mapping model using source and destination model. In that case set,
NSDictionary *options=[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],NSMigratePersistentStoresAutomaticallyOption,[NSNumber numberWithBool:NO],NSInferMappingModelAutomaticallyOption, nil];
Create douce metadata with following code
if (sourceMetadata) {
NSString *configuration = nil;
NSManagedObjectModel *destinationModel = [self.persistentStoreCoordinator managedObjectModel];
//Our Source 1 is going to be incompatible with the Version 2 Model, our Source 2 won't be...
BOOL pscCompatible = [destinationModel isConfiguration:configuration compatibleWithStoreMetadata:sourceMetadata];
NSLog(@"Is the STORE data COMPATIBLE? %@", (pscCompatible==YES) ?@"YES" :@"NO");
if (pscCompatible == NO) {
migrationSuccess = [self performMigrationWithSourceMetadata:sourceMetadata toDestinationModel:destinationModel];
}
}
else {
NSLog(@"checkForMigration FAIL - No Source Metadata! \nERROR: %@", [error localizedDescription]);
}
and implement the following function
- (BOOL)performMigrationWithSourceMetadata :(NSDictionary *)sourceMetadata toDestinationModel:(NSManagedObjectModel *)destinationModel
{
BOOL migrationSuccess = NO;
//Initialise a Migration Manager...
NSManagedObjectModel *sourceModel = [NSManagedObjectModel mergedModelFromBundles:nil
forStoreMetadata:sourceMetadata];
//Perform the migration...
if (sourceModel) {
NSMigrationManager *standardMigrationManager = [[NSMigrationManager alloc] initWithSourceModel:sourceModel
destinationModel:destinationModel];
//Retrieve the appropriate mapping model...
NSMappingModel *mappingModel = [NSMappingModel mappingModelFromBundles:nil
forSourceModel:sourceModel
destinationModel:destinationModel];
if (mappingModel) {
NSError *error = nil;
NSString *storeSourcePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent:@"Recipes.sqlite"];
NSURL *storeSourceUrl = [NSURL fileURLWithPath: storeSourcePath];
NSString *storeDestPath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent:@"Recipes2.sqlite"];
NSURL *storeDestUrl = [NSURL fileURLWithPath:storeDestPath];
//Pass nil here because we don't want to use any of these options:
//NSIgnorePersistentStoreVersioningOption, NSMigratePersistentStoresAutomaticallyOption, or NSInferMappingModelAutomaticallyOption
NSDictionary *sourceStoreOptions = nil;
NSDictionary *destinationStoreOptions = nil;
migrationSuccess = [standardMigrationManager migrateStoreFromURL:storeSourceUrl
type:NSSQLiteStoreType
options:sourceStoreOptions
withMappingModel:mappingModel
toDestinationURL:storeDestUrl
destinationType:NSSQLiteStoreType
destinationOptions:destinationStoreOptions
error:&error];
NSLog(@"MIGRATION SUCCESSFUL? %@", (migrationSuccess==YES)?@"YES":@"NO");
}
}
else {
//TODO: Error to user...
NSLog(@"checkForMigration FAIL - No Mapping Model found!");
abort();
}
return migrationSuccess;
}
The mapping model generated by Xcode 4 does not produce the correct hashes needed for the migration to occur. You can compare the output from the migration log to your mapping file's hashes with the code below:
NSString *mappingModelPath = [[NSBundle mainBundle] pathForResource:@"MappingFile" ofType:@"cdm"];
NSMappingModel *mappingModel = [[NSMappingModel alloc] initWithContentsOfURL:[NSURL fileURLWithPath:mappingModelPath]];
for (NSEntityMapping *entityMapping in mappingModel.entityMappings) {
NSLog(@"%@: %@", entityMapping.sourceEntityName, entityMapping.sourceEntityVersionHash);
NSLog(@"%@: %@", entityMapping.destinationEntityName, entityMapping.destinationEntityVersionHash);
}
You'll see that these do not match the hashes in the migration log output.
The workaround is to generate the mapping file in Xcode 5.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With