1

In upgrading my application from v1 to v2, I've made a few small changes to the Core Data Model. The changes are just adding new attributes to the models.

I have versioned the data model with the before and after changes and implemented the following code in my App Delegate:

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    if (__persistentStoreCoordinator != nil)
    {
        return __persistentStoreCoordinator;
    }

    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"ISDEmployees.sqlite"];

    NSLog(@"storeURL:%@",storeURL);

    NSError *error = nil;

    // Create a dictionary for automatic lightweight core data migration
    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                             [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
                             [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, 
                             nil];

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

    // Set up the persistent store and migrate if needed
    if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error])
    {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }

    return __persistentStoreCoordinator;
}

Basically the standard persistentStoreCoordinator with the addition of the migration options. This code works great and my database is successfully updated. The problem that I'm having is that after the database update, I need to refresh all of the data in the database so that the new columns are populated. I was thinking that I would delete the data from the relevant entities/tables and force the application to re-download a new dataset with the added columns/attributes.

I'm not sure how/where to perform the delete/updates. The general application flow is this:

  • Log in with validation against an web API
  • On successful login, call the API and get latest added/updated records.
  • Display the updated data

I know I can check to see if a migration is needed by adding this code to persistentStoreCoordinator:

// Get the current data store meta data
BOOL migrationNeeded = NO;
NSDictionary *existingStoreData = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:NSSQLiteStoreType URL:storeURL error:&error];

if (existingStoreData) 
{
    // Check to see if new model is not the same as the existing mode, meaning a migration is required
    if (![self.managedObjectModel isConfiguration:nil compatibleWithStoreMetadata:existingStoreData]) 
    {
        migrationNeeded = YES;
    }
}

Any help would be greatly appreciated!!

Update #1:

Based on the feedback below, I've made the following changes:

Changed the migrationNeeded from a local to a public class variable on the AppDelegate. On the Login View, I've added the following method:

- (void)checkForDatabaseMigration
{
    // Get a copy of the managed object context. If a migration is needed, it will kick it off
    NSManagedObjectContext *managedObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];

    if ([(AppDelegate *)[[UIApplication sharedApplication] delegate] migrationNeeded]) 
    {
        // Delete all data from the table
    }

    managedObjectContext = nil;
}

Does that seem right? The code works and the data is removed after migration and a fresh copy is inserted. I just hate to check for migration each time the application starts.

Allen
  • 1,165
  • 2
  • 11
  • 23

1 Answers1

0

If you know how to determine when to delete old data, all you need is to fetch all the enteties you need and delete them. Here is how you do that(for example, if you want to delete all Man enteties):

    NSFetchRequest * request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:@"Man" inManagedObjectContext:myContext]];
[request setIncludesPropertyValues:NO]; //only fetch the managedObjectID

NSError * error = nil;
NSArray * men = [myContext executeFetchRequest:request error:&error];
//error handling goes here
for (NSManagedObject * man in men) {
  [myContext deleteObject:man];
}
NSError *saveError = nil;
[myContext save:&saveError];
//more error handling here
Nikita Pestrov
  • 5,876
  • 4
  • 31
  • 66
  • Thanks Nikita, that pointed me in the right direction. I've amended by question with a possible solution. – Allen Jun 13 '12 at 20:21