Core Data Migration: methods in NSEntityMigrationPolicy not called.
Asked Answered
R

2

9

I have two model versions - 12 and 13. Then I created a xcmappingmodel-File with source 12 and destination 13.

I subclassed NSEntityMigrationPolicy and added my class to the mappingmodel-File to the entity desired.

@interface EndDateMigrationPolicy : NSEntityMigrationPolicy

enter image description here

After installing and older version (11) on my device, I install the current status with model version 13 - the app runs, but my migration methods aren't called. Am I missing something?

EDIT: is it correct to use these options?

    NSDictionary *options = @{NSMigratePersistentStoresAutomaticallyOption: @YES,
                         NSInferMappingModelAutomaticallyOption: @YES};
Raker answered 21/11, 2012 at 12:49 Comment(1)
I have a similar issue. Did you solve this problem. If yes, please post the answer.Lunula
T
3

I'm going to try to answer you as best I can, I have suffered through core data migrations quite a few times so I know how painful it is. For one you can not hope to have to have your migration work with those options because what you are trying to do is actually not a lightweight migration and yet you are telling it to do a lightweight migration.

Basically let's say you need for some reason to have a non lightweight migration between 2 versions, in your case 11 and 12. What you will need to do is do:

1->12 lightweight 12->13 custom migration 13->(future version) lightweight migration

There might be a better solution but I have not yet found it.

Here is some code to help you out (the hardest parts, I can't remember everything offhand), before this code I copy the database to a backup, so basically the back up database is your old one and the store is your new one (which is empty)

NSString *path = [[NSBundle mainBundle] pathForResource:<YOUR MODEL NAME> ofType:@"cdm"];

NSURL *backUpURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"<YOUR MODEL NAME>MigrationBackUp.sqlite"]; //or whatever you want to call your backup
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"<YOUR MODEL NAME>.sqlite"];
NSError *err2 = nil;
NSDictionary *sourceMetadata2 =
[NSPersistentStoreCoordinator metadataForPersistentStoreOfType:NSSQLiteStoreType
                                                           URL:backUpURL
                                                         error:&err2];
NSManagedObjectModel *sourceModel2 = [NSManagedObjectModel mergedModelFromBundles:[NSArray arrayWithObject:[NSBundle mainBundle]]

                                                                 forStoreMetadata:sourceMetadata2];
NSManagedObjectModel *destinationModel2 = [self managedObjectModelForVersion:@"1.4"]; //Yeah your gonna have to get your mapping model , I'll give you this code too later
NSString *oldModel = [[sourceModel2 versionIdentifiers] anyObject];
NSLog(@"Source Model : %@",oldModel);
NSMappingModel *mappingModel = [[NSMappingModel alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path]];

if (mappingModel != nil) {
    for (NSString * identifier in [mappingModel entityMappings]) {
        NSLog(@"Mapping > %@",identifier);
    }
}

Then just make a migrator with your source and destination.

This is also a difficult part afterwards:

    BOOL success = [migrator migrateStoreFromURL:backUpURL
                                        type:NSSQLiteStoreType
                                     options:nil
                            withMappingModel:mappingModel
                            toDestinationURL:storeURL
                             destinationType:NSSQLiteStoreType
                          destinationOptions:nil
                                       error:&err2];

Last but not least (I said I'd give it to you before):

- (NSManagedObjectModel *)managedObjectModelForVersion:(NSString*)version {

NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"Model" ofType:@"momd"];
if (BETWEEN_INEX(version, @"1.0", @"1.4")) {
    modelPath = [modelPath stringByAppendingPathComponent:@"Model"];
    modelPath = [modelPath stringByAppendingPathExtension:@"mom"];
} else if (BETWEEN_INEX(version, @"1.4", @"1.5")) {
    modelPath = [modelPath stringByAppendingPathComponent:@"Model 2"];
    modelPath = [modelPath stringByAppendingPathExtension:@"mom"];
} else if (BETWEEN_INEX(version, @"1.5", @"1.6")) {
    modelPath = [modelPath stringByAppendingPathComponent:@"Model 3"];
    modelPath = [modelPath stringByAppendingPathExtension:@"mom"];
} else if (BETWEEN_INEX(version, @"1.6", @"1.7")) {
    modelPath = [modelPath stringByAppendingPathComponent:@"Model 4"];
    modelPath = [modelPath stringByAppendingPathExtension:@"mom"];
}
NSURL *modelURL = [NSURL fileURLWithPath:modelPath];
NSManagedObjectModel * oldManagedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
NSSet *vIdentifiers = [oldManagedObjectModel versionIdentifiers];
for (NSString * identifier in vIdentifiers) {
    NSLog(@"Old Model : %@",identifier);
}
return [oldManagedObjectModel autorelease];

}

I know these are just snippets of code, but I really hope it helps you solve your problem, if you need anything else just ask.

Terris answered 23/11, 2012 at 18:51 Comment(2)
is this really the best way? isn't there something easier to "mix" lightweight and complex migrations?Raker
I'm pretty sure it's the easiest way. A migration model can only reference 2 schema versions. The complicated part is not really mixing the migrations, that's kinda easy, you just do them serially, the complex migration is where the difficulty really lies.Terris
V
1

Your NSEntityMigrationPolicy is not being called because you set NSInferMappingModelAutomaticallyOption to @(YES) - it should be @(NO)

Voile answered 15/1, 2013 at 3:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.