50

I would like to make my app able to do an automatic lightweight migration when I add new attributes to my core data model.

In the guide from Apple this is the only info on the subject I could find:

Automatic Lightweight Migration

To request automatic lightweight migration, you set appropriate flags in the options dictionary you pass in addPersistentStoreWithType:configuration:URL:options:error:. You need to set values corresponding to both the NSMigratePersistentStoresAutomaticallyOption and the NSInferMappingModelAutomaticallyOption keys to YES:

NSError *error;
NSURL *storeURL = <#The URL of a persistent store#>;
NSPersistentStoreCoordinator *psc = <#The coordinator#>;
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
    [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
    [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
 
if (![psc addPersistentStoreWithType:<#Store type#>
    configuration:<#Configuration or nil#> URL:storeURL
    options:options error:&error]) {
    // Handle the error.
}

My NSPersistentStoreCoordinator is initialized in this way:

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
    
    if (persistentStoreCoordinator != nil) {
        return persistentStoreCoordinator;
    }
    
    NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: @"FC.sqlite"]];
    
    NSError *error = nil;
    persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error]) {

        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }    
    
    return persistentStoreCoordinator;
}

I am having trouble seeing where and how I should add the Apple code to get the Automatic Lightweight Migration working?

Community
  • 1
  • 1
RickiG
  • 11,380
  • 13
  • 81
  • 120
  • **Swift 4 version** follow this link - https://stackoverflow.com/questions/51519516/swift-4-in-xcode-9-how-to-lightweight-core-data-migration/52090943#52090943 – Gulshan Kumar Sep 17 '18 at 08:23

5 Answers5

93

This is what I did to make Automatic Lightweight Migration (Source: http://brainwashinc.wordpress.com/2010/01/18/iphone-coredata-automatic-light-migration/)

1. Set the Persistent Store options for automatic migration in the app delegate.

Change your persistentStoreCoordinator creation to this (replace YOURDB):

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {

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

  NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: @"YOURDB.sqlite"]];

  // handle db upgrade
  NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
  [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
  [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];

  NSError *error = nil;
  persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
  if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]) {
    // Handle error
  }

  return persistentStoreCoordinator;
}

2. Version your Data Model and Edit the new file.

Select your xcdatamodel file Design -> Data Model -> Add Model Version (expand your xcdatamodeld item) Select the “2″ (or later) file, Design -> Data Model -> Set Current Version (edit this version)

3. Specify the momd resource in app delegate.

Change your managedObjectModel implementation to this (replace YOURDB)

- (NSManagedObjectModel *)managedObjectModel {

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

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

  return managedObjectModel;
}
iwasrobbed
  • 46,496
  • 21
  • 150
  • 195
willi
  • 6,559
  • 4
  • 30
  • 27
  • Hi Willi Thank you so much for taking the time to fill out all the gaps:) – RickiG Feb 23 '10 at 12:45
  • I tried the above code and I get this error... *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSURL initFileURLWithPath:]: nil string parameter' any ideas? – OscarTheGrouch Aug 13 '10 at 18:10
  • @oscarTheGrouch -- most likely the file path returned from NSBundle is empty owing to misspelling the name or type of the resource or the file not actually being there. – TechZen Jun 11 '11 at 13:56
  • 14
    You could link to the source: http://brainwashinc.wordpress.com/2010/01/18/iphone-coredata-automatic-light-migration/ – pablasso Jul 06 '11 at 18:43
  • work in xcode4 ? How to generate the momd file ? is that .xcdatamodel file ? – Forrest Jul 08 '11 at 10:19
  • 8
    Step 2 is slightly different in XCode 4.3 (at least): to set the current schema version you must first select the data model which appears like a folder containing the "old" and "new" versions of your data model. You'll see it in the project navigator. Select this "folder" and in the attributes inspector on the right you will find "Versioned Core Data Model" – pick the desired one from the dropdown box there. – Swizzlr Apr 08 '12 at 12:30
  • Do I need to create new datamodel if I have just added an attribute to my existing entity? If I don't need to create new model..is these versioning of datamodel needs to be done? – AKG May 22 '12 at 16:15
  • 1
    Step2 XCODE 4.6 Update: mark .xcdatamodeld->Editor-> Add Model Version – Amandir Aug 01 '13 at 20:00
7

At first, the above solution didn't work for me. The returned managedObjectModel was 0x0. I think this is because I renamed the file names of the different model files. If you follow the instructions above to the letter then it all works.

However if you do change model file names then you can select the "current" model file manually: Lets say your original model file was MYMODEL.xcdatamodel after doing the add model step above this turns into a directory MY.xcdatamodeld and below it you have MYMODEL.xcdatamodel and MYMODEL 2.xcdatamodel rename the new model file to whatever you want, for example lets say you removed the space to MYMODEL2.xcdatamodel and edit its content. Now in the above code do

NSString *path = [mainBundle pathForResource:@"MYMODEL2" ofType:@"mom" inDirectory:@"MYMODEL.momd"];
udibr
  • 1,069
  • 7
  • 5
  • Hi udibr. Thanks, I actually ran into exactly that problem:) I also ran into the problem of trying to change the name of my app.sql file, which will also crash everything when dealing with a project that is already up an running. As you write, If you follow the instruction to the letter, everything works fine, so up-vote for willi:) – RickiG Mar 15 '10 at 15:34
  • Whenever I try to use the above code... I get this error... *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSURL initFileURLWithPath:]: nil string parameter' any ideas? – OscarTheGrouch Aug 13 '10 at 18:10
1

I think this adds onto the last answer.

I found the usage of the bundle resource and .sqlite names really confusing at first. Does the bundle resource name change with the version change? Does the .sqlite name change? I've now got my migration working, and learned that the bundle model name refers to the name of the directory/folder in XCode containing all the models, not the name of the model versions within that directory.

When you give a modelResource name to:

NSURL *modelURL = [[NSBundle mainBundle] URLForResource:modelResource withExtension:@"momd"];
NSManagedObjectModel *theManagedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];

That modelResource name is the directory/folder for the models in Xcode.

When you do:

    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:storeFileName];
    NSError *error = nil;

    if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {
        // handle error
    }

The storeFileName is the name of your .sqlite file in the Documents folder/directory (this is not in the bundle).

Also, when you migrate from one model version to another model version, by default, the .sqlite file name remains the same.

Chris Prince
  • 7,288
  • 2
  • 48
  • 66
0

Swift 3 Solution

1. Set the Persistent Store options for automatic migration in the app delegate.

Change your persistentStoreCoordinator creation to this (replace SingleViewCoreData.sqlite):

lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {


let coordinator: NSPersistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.appendingPathComponent("SingleViewCoreData.sqlite")

let options = [
  NSMigratePersistentStoresAutomaticallyOption : Int(true),
  NSInferMappingModelAutomaticallyOption : Int(true)
]

do {

  try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: options)

} catch {

  print(error)
}

return coordinator

}()

2. Version your Data Model and Edit the new file.

Select your xcdatamodel file Editor>Add Model Version - add a name for your new model

Ben Sullivan
  • 2,134
  • 1
  • 18
  • 32
0

Oscar, in response to your issue, I found the same thing initially. I would suggest deleting and re-adding the new .xcdatamodeld file to your project, and then rebuilding. Hope that helps!

gemmakbarlow
  • 832
  • 12
  • 23