1

I followed a tutorial here on core data standard migratioin:

http://mipostel.com/index.php/home/70-core-data-migration-standard-migration-part-2

And then one here on doing it with multiple passes:

Example or explanation of Core Data Migration with multiple passes?

Which has given me the resulting code here:

- (NSManagedObjectContext *)managedObjectContext {

    if (managedObjectContext != nil) {
        return managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        managedObjectContext = [TICDSSynchronizedManagedObjectContext new];
        [managedObjectContext setPersistentStoreCoordinator: coordinator];
    }
    return managedObjectContext;
}
- (NSManagedObjectModel *)managedObjectModel {

    if (managedObjectModel != nil) {
        return managedObjectModel;
    }

    //    managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];
    NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"EntryDatabase" ofType:@"momd"];
    NSURL *modelURL = [NSURL fileURLWithPath:modelPath];
    managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];

    return managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {

    if (persistentStoreCoordinator != nil) {
        return persistentStoreCoordinator;
    }

    NSString *storePath = [[self applicationDocumentsDirectory]
                           stringByAppendingPathComponent:@"CoreDataStore.sqlite"];

    NSFileManager *fileManager = [NSFileManager defaultManager];
    // If the expected store doesn't exist, copy the default store.
    NSLog(@"file exists at path: %@, %i", storePath, [fileManager fileExistsAtPath:storePath]);
    if (![fileManager fileExistsAtPath:storePath]) {
        NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:@"CoreDataStore" ofType:@"sqlite"];
        if (defaultStorePath) {
            [fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
        }
    }

    persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];

    NSURL *storeUrl = [NSURL fileURLWithPath:storePath];
    NSError *error;
    NSDictionary *pscOptions = [NSDictionary dictionaryWithObjectsAndKeys:
                                [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
                                [NSNumber numberWithBool:NO], NSInferMappingModelAutomaticallyOption,
                                nil];

    if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
                                                  configuration:nil
                                                            URL:storeUrl
                                                        options:pscOptions
                                                          error:&error]) {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    }

    return persistentStoreCoordinator;
}

- (BOOL)checkForMigration
{
    BOOL migrationSuccess = NO;
    NSString *storeSourcePath = [[self applicationDocumentsDirectory]
                                 stringByAppendingPathComponent:@"CoreDataStoreNew.sqlite"];
    NSFileManager *fileManager = [NSFileManager defaultManager];

    if (![fileManager fileExistsAtPath:storeSourcePath]) {
        //Version 2 SQL has not been created yet, so the source is still version 1...
        storeSourcePath = [[self applicationDocumentsDirectory]
                           stringByAppendingPathComponent:@"CoreDataStore.sqlite"];
    }

    NSURL *storeSourceUrl = [NSURL fileURLWithPath: storeSourcePath];
    NSError *error = nil;
    NSDictionary *sourceMetadata = [NSPersistentStoreCoordinator
                                    metadataForPersistentStoreOfType:NSSQLiteStoreType
                                    URL:storeSourceUrl
                                    error:&error];
    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]);
    }
    return migrationSuccess;
}


- (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];

        NSArray *mappingModelNames = [NSArray arrayWithObjects:@"StepOne", @"StepTwo", nil];
        NSDictionary *sourceStoreOptions = nil;

        NSString *destinationStorePath = [[self applicationDocumentsDirectory]
                                          stringByAppendingPathComponent:@"CoreDataStoreNew.sqlite"];

        NSURL *destinationStoreURL = [NSURL fileURLWithPath: destinationStorePath];

        NSString *destinationStoreType = NSSQLiteStoreType;

        NSDictionary *destinationStoreOptions = nil;

        for (NSString *mappingModelName in mappingModelNames) {

            NSError *error;

            NSURL *fileURL = [[NSBundle mainBundle] URLForResource:mappingModelName withExtension:@"cdm"];

            NSMappingModel *mappingModel = [[NSMappingModel alloc] initWithContentsOfURL:fileURL];

            migrationSuccess = [standardMigrationManager migrateStoreFromURL:destinationStoreURL
                                                                        type:NSSQLiteStoreType
                                                                     options:sourceStoreOptions
                                                            withMappingModel:mappingModel
                                                            toDestinationURL:destinationStoreURL
                                                             destinationType:destinationStoreType
                                                          destinationOptions:destinationStoreOptions
                                                                       error:&error];

            NSLog(@"Error: %@", error);
        }

    }

    return migrationSuccess;

}

Still, the app runs out of memory and crashes at the line in persistentStoreCoordinator:

if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
                                              configuration:nil
                                                        URL:storeUrl
                                                    options:pscOptions
                                                      error:&error]) {
Community
  • 1
  • 1
Andrew
  • 15,935
  • 28
  • 121
  • 203
  • Didn't you post this same question a while ago? If not some else posted almost the exact same thing. I do have an idea now that I didn't have earler, as I used a technique that should work here too (but its not trivial) Will write up in text editor then add later. – David H Sep 23 '12 at 14:42
  • @DavidH I've asked other questions on the same code. Look forward to seeing your alternative. – Andrew Sep 23 '12 at 16:22

2 Answers2

0

I remember having followed this tutorial as well.

Actually I found that this is not the right way of doing it. by the way did you read the very last comment on that tutorial and tried removing the lines mentioned? Maybe that fixes it in your case?

Anyway in my case I realized (after having followed this tutorial, and the modification did not help) that there was a much much easier way.

In your case I do not know, however, if this little modification does not fix it, I would really look for another reference - since this really is not the correct way of doing it and leads you in wrong direction, since it tries to do everything itself instead of using the logic behind Apple's core data migration procedure as I found out the hard way.

user387184
  • 10,953
  • 12
  • 77
  • 147
  • Did remove those lines. Which alternative did you discover? – Andrew Sep 23 '12 at 16:19
  • as it turned out, my case was quite easy - so all I had to do was to use the normal migration which added all the new attributes and relationships - I am not sure if it applies to you. Sorry, but the only advice I can give is to really recheck your new datamodel and see if you REALLY need a more complex translation... - in my case (after I understood what the leightweight can do) that's all I needed – user387184 Sep 23 '12 at 16:35
0

The problem I solved several years ago was to essentially take a subtree out of one repository and dup it in another, and what I did should work for you. I was doing this on the Mac so memory not an issue, but by proper faulting and reducing memory as per the Core Data Programming Guide 'Reducing Memory Overhead', you should be able to get this to work.

The following solution is predicated on the assumption that the MOMs are not all that different. Let me introduce the term 'A' for existing context and 'B' for the new one.

1) the first step is to duplicate every object in A in B. If the class has stayed the same, fine. This means that for every object, you need a list of all the values from the entity. I suggest using keys - an array of attribute keys for each entity type, which will make it easier to code (if you can). Otherwise, you can actually get the keys from the MOM - which is what I did in my old code.

Now the critical step - you must create a translation dictionary (perhaps you will need two - I did), that for every entity in A you know the corresponding one in 'B'. You can use the 'objectID' property (but for B do not do a save til you are finished as once you save this value changes).

2) Now that you have completely recreated all entities, you need to 'wire' them up - so all the relationships are properly set. Again, create some arrays of keys for each entity type, and then in a loop you look at each relationship in 'A', get the entity it points to, use the translation table to find the corresponding one in 'B', and set the value in 'B'.

Voila! Its done. You obviously make additions are deletions are you do the above to mirror the changes from A - B.

Again I didn't need to worry so much about memory on the Mac so didn't need tricks to keep memory down. I believe faulting ('refreshObject:mergeChanges:') will help you but again never had to do this (and even so, probably just object that are large in size).

David H
  • 40,852
  • 12
  • 92
  • 138