Search code examples
ioscore-datacore-data-migration

Open a CoreData store with a specific (non current) MOM version


I have a variety of breaking schema changes in a CoreData model version. Since the local schema is simply a cache of network data I'm quite happy to delete the sqlite file and force a redownload for this version of the iOS app.

However...

There is one entity in the DB which is written by the user and should not be lost.

In the new version of the app, this volatile data is no longer written to the CoreData store, but recorded on the filesystem.

I am working on going step-by-step so that (say) version 2.0.1 of the app uses the v1 schema and copies the data, but has no breaking schema changes, and then version 2.0.2 of the app can add the schema changes and delete the DB, safe in the knowledge that the data has been removed but due to the way the appstore updates work, its not possible to guarantee a user goes from .0-.1-.2. They may go straight from .0 to .2 and hit the breaking changes when they try to open the core data store.

Any thoughts, pointers, suggestions appreciated.

UPDATE: I am wondering if 'faking' a migration might be the way to go. A custom migration policy that ignores everything but the volatile data and doesn't actually migrate it, just writes it to the filesystem. Once that has run, I'd have the volatile data in the filesystem and an empty DB based on the new schema.


Solution

  • I also think you might be better with faking the migration, here's some code to get that mom that you want. Just replace EntryModel with your model name, and tweak the versions. Then use the "-(id)initWithManagedObjectModel" method on NSPersistentStoreCoordinator to get your store coordinator on the model you want.

    - (NSManagedObjectModel *)managedObjectModelForVersion:(NSString*)version {
    
    NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"EntryModel" ofType:@"momd"];
    if (BETWEEN_INEX(version, @"1.0", @"1.4")) {
        modelPath = [modelPath stringByAppendingPathComponent:@"EntryModel"];
        modelPath = [modelPath stringByAppendingPathExtension:@"mom"];
    } else if (BETWEEN_INEX(version, @"1.4", @"1.5")) {
        modelPath = [modelPath stringByAppendingPathComponent:@"EntryModel 2"];
        modelPath = [modelPath stringByAppendingPathExtension:@"mom"];
    } else if (BETWEEN_INEX(version, @"1.5", @"1.6")) {
        modelPath = [modelPath stringByAppendingPathComponent:@"EntryModel 3"];
        modelPath = [modelPath stringByAppendingPathExtension:@"mom"];
    } else if (BETWEEN_INEX(version, @"1.6", @"1.7")) {
        modelPath = [modelPath stringByAppendingPathComponent:@"EntryModel 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];
    }
    

    also this might be useful:

    #define GREATER_THAN(w,v)              ([w compare:v options:NSNumericSearch] == NSOrderedDescending)
    #define GREATER_THAN_OR_EQUAL_TO(w,v)  ([w compare:v options:NSNumericSearch] != NSOrderedAscending)
    #define LESS_THAN(w,v)                 ([w compare:v options:NSNumericSearch] == NSOrderedAscending)
    #define LESS_THAN_OR_EQUAL_TO(w,v)     ([w compare:v options:NSNumericSearch] != NSOrderedDescending)
    #define BETWEEN_INCLUDE(w,v,z)     (GREATER_THAN_OR_EQUAL_TO(w,v) && LESS_THAN_OR_EQUAL_TO(w,z))
    #define BETWEEN_EXCLUDE(w,v,z)     (GREATER_THAN(w,v) && LESS_THAN(w,z))
    #define BETWEEN_INEX(w,v,z)     (GREATER_THAN_OR_EQUAL_TO(w,v) && LESS_THAN(w,z))
    #define BETWEEN_EXIN(w,v,z)     (GREATER_THAN(w,v) && LESS_THAN_OR_EQUAL_TO(w,z))