7

I'm making quite a few changes to the object model for our app upgrade release ie. entities added / removed, new attributes and relationships. It seems like the work would really add up for a proper core data migration. Since the data primarily serves as a cache to enhance the offline browsing experience. at this point doesnt really need migration I would think it would be a whole lot simpler if it were just blown away and recreated.

Based on various posts I've come across on this topic the general strategy is to

  • detect that the model has changed (by catching the exception during initialization of the managedObjectContext)
  • delete the persistent store (in our case on iOS the sqlite file)
  • reinitialize an objectModel with the latest schema reinitialize a persistent store with the new model

This is the code that reinitializes the objectModel

- (NSManagedObjectModel *)managedObjectModel {

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

NSString *path = [[NSBundle mainBundle] pathForResource:@"<model name>" ofType:@"momd"];
NSURL *momURL = [NSURL fileURLWithPath:path];
managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:momURL];

return managedObjectModel; 
}

and recreating the objectModel and store with

objectManager = [RKObjectManager objectManagerWithBaseURL:
                     [NSString stringWithFormat:@"http://%@/v3", 
                      [[NSBundle mainBundle] objectForInfoDictionaryKey:@"APIDomain"]]];     
NSManagedObjectModel *objectModel = [self managedObjectModel];
objectManager.objectStore = [RKManagedObjectStore objectStoreWithStoreFilename:storeName usingSeedDatabaseName:nil managedObjectModel:objectModel delegate:nil]; 

However, I get the following error:

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '+entityForName: could not locate an NSManagedObjectModel for entity name 'UTCity''

I feel like it's pretty close since restarting the app succeeds in creating a new store runs correctly.

-PF

LU RD
  • 34,438
  • 5
  • 88
  • 296
PFaunik
  • 161
  • 1
  • 7

2 Answers2

6

I think I've been able to do what you're describing by implementing a method from RKManagedObjectStoreDelegate. The method is called when the persistent store creation fails. I simply delete the persistent store when this method is called. RestKit seems to recover from this just fine. I assume it created the new blank store next time it needs it.

- (void)managedObjectStore:(RKManagedObjectStore *)objectStore didFailToCreatePersistentStoreCoordinatorWithError:(NSError *)error {
    [objectStore deletePersistentStore];
}

RKManagedObjectStore attempts to create the persistent store at initialization, so you'll need to initialize your instance of RKManagedObjectStore via one of the methods that accepts a delegate object. I just passed in my app delegate.

So far, this seems to work. As I continue developing, we'll see if it continues to do so.

CharlieMezak
  • 5,999
  • 1
  • 38
  • 54
  • Looks like `-[RKManagedObjectStore resetPersistentStores:]` may be the equivalent in RestKit 0.20.x. – Josh Brown Jun 20 '13 at 21:03
  • 1
    This delegate function managedObjectStore:didFailToCreatePersistentStoreCoordinatorWithError: seems to be gone in 0.20. Do you know of a replacement? – mosca1337 Feb 28 '14 at 00:41
3

Here is a solution to delete the persistent store altogether when migration fails.

    // Core Data Persistent Store
    NSError *error;
    NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"Data.sqlite"];
    NSPersistentStore __unused *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:storePath
                                                                              fromSeedDatabaseAtPath:nil
                                                                                   withConfiguration:nil
                                                                                             options:@{NSInferMappingModelAutomaticallyOption: @YES, NSMigratePersistentStoresAutomaticallyOption: @YES}
                                                                                               error:&error];

    // Reset the persistant store when the data model changes
    if (error) {

        [[NSFileManager defaultManager] removeItemAtPath:storePath
                                                   error:nil];

        NSPersistentStore __unused *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:storePath
                                                                                  fromSeedDatabaseAtPath:nil
                                                                                       withConfiguration:nil
                                                                                                 options:nil
                                                                                                   error:nil];
    }
mosca1337
  • 2,409
  • 3
  • 24
  • 27