Core Data versioning and migrating with custom policy
Asked Answered
M

2

5

I've found documentation very limited and my problem solution nowhere.

I need to add new entity (with relationship to existing one). Also add and rename some of attributes of existing entity.

Lightweighted example:

Old model has one entity Item with one attribute name.

In new model I want to Item to have one new attribute dateAdded and rename name to title. At this point, if dateAdded would be optional or given default value, I could use lightweight migration feature. Correct me if I am wrong.

But I also want to add new List entity with title attribute. And add to-many relationship. List can be empty or have many items. Item have to refer to exactly one list.

So I am confused in what everything I have to do and what order.

  1. Turn on migration with lightweighted migration feature disabled (NSDictionary* options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:NO], NSInferMappingModelAutomaticallyOption, nil];).

  2. Create a new version of model. There I do changes I want to.

  3. Create a new mapping model. Source is old model, target is new model. In ItemToItem I set title to $source.name.

Besides Xcode is still crashing on working with mapping model I do not know what to do next. I think I have to create one instance of List in context and make all items referring to it because of relationship policy. And I thing I should use custom NSEntityMigrationPolicy to do this. Any help to accomplish this challenge?

Maxwellmaxy answered 11/8, 2011 at 13:53 Comment(0)
M
9

Well, I did it...

My first 3 steps were correct. Continue scenario:

ADD4. Do an ItemToItemMigrationPolicy, a subclass of NSEntityMigrationPolicy. Override:

- (BOOL)beginEntityMapping:(NSEntityMapping *)mapping manager:(NSMigrationManager *)manager error:(NSError **)error
{
    List* list = (List*)[NSEntityDescription insertNewObjectForEntityForName:@"List" inManagedObjectContext:[manager destinationContext]];
    list.name = @"Default list";

    return YES;
}

- (BOOL)createDestinationInstancesForSourceInstance:(NSManagedObject *)sInstance entityMapping:(NSEntityMapping *)mapping manager:(NSMigrationManager *)manager error:(NSError **)error
{
    Item* item = (Item*)[NSEntityDescription insertNewObjectForEntityForName:[mapping destinationEntityName] inManagedObjectContext:[manager destinationContext]];
    item.dateAdded = [NSDate date];
    task.title = [sInstance valueForKey:@"name"];

    [manager associateSourceInstance:sInstance withDestinationInstance:item forEntityMapping:mapping];

    return YES;
}

- (BOOL)createRelationshipsForDestinationInstance:(NSManagedObject *)dInstance entityMapping:(NSEntityMapping *)mapping manager:(NSMigrationManager *)manager error:(NSError **)error
{

    NSFetchRequest* fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"List"];
    NSPredicate* predicate = [NSPredicate predicateWithFormat:@"name LIKE 'Default list'"];
    fetchRequest.predicate = predicate;

    NSError* fetchRequestError = nil;
    NSArray* listsArray = [manager.destinationContext executeFetchRequest:fetchRequest error:&fetchRequestError];
    if (fetchRequestError) {
        NSLog(@"%@", fetchRequestError.localizedDescription);
    }
    List* list = [listsArray lastObject];

    ((Item*)dInstance).list = list;

    return YES;
}

ADD5. In Xcode in mapping model set ItemToItem migration policy to custom with ItemToItemMigrationPolicy value.

ADD6. Make your new model version current and generate (replace) classes from new or changed entities.

ADD7. Do changes in your code, for example item.name no more works. Now it is item.title. Clean project and run.

Maxwellmaxy answered 12/8, 2011 at 11:57 Comment(2)
Why lightweight need to be disabled for heavyweight? It is working for me. So my dream code is wrong?!!Rinehart
I did all the steps you have mentioned but in my case, SQLite got corrupted and methods have been called from NSEntityMigrationPolicy. I added migration DEBUG log, from the log it says migration has been completed but later it crashed while trying to fetch data since SQlite got corrupted.Mckinnie
A
1

If you are adding a new entity, then you will need to use a custom mapping model and turn off lightweight migration.

One important thing. When working with migration, make sure you start with a fresh example of the existing persistent store every time especially if you have had crashes. If you don't, you can corrupt the store which will cause errors to snowball.

Aegean answered 11/8, 2011 at 14:23 Comment(3)
I'm midway into my migration initiative. AFAIK, you can avoid store corruption issues my checking for and removing the temporary store before attempting migration. This will also ensure your app isn't trying to migration with a corrupted store in the case where migration fails to user intervention, power loss, etc.Paleobotany
@Paleobotany Hey, could you please elaborate more what are the reasons behind SQLite corruption, I am stuck here, in my case data got corrupted and not a single method called from NSEntityMigrationPolicy subclass. [error] error: (11) Fatal error. The database at /var/mobile/Containers/Data/Application/8940278E-4AA7-4318-8EDA-3EB1B9B8EE1A/Documents/Lido_mPilot.sqlite is corrupted. SQLite error code:11,Mckinnie
It's been 6 years since I worked with this stuff. Here's a list of common SQLite corruption scenarios: sqlite.org/howtocorrupt.htmlPaleobotany

© 2022 - 2024 — McMap. All rights reserved.