Using NSMigrationManager to append data to Core Data Persistent Store
Question
I would like to append the contents from one sqlite file (that has been created using Core Data with a Model A) to another sqlite store that is used by my application (which uses the same Model A). The idea is to quickly import large amounts of data.
The problem I am facing is that the code below only works once. When I try to run the same code twice the application will crash in the line I marked with my comment. Any help would be greatly appreciated.
NSError **err;
NSURL *importURL = [NSURL fileURLWithPath:[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent: @"import.sqlite"]];
NSURL *storeURL = [NSURL fileURLWithPath:[[self applicationDocumentsDirectory] stringByAppendingPathComponent: @"applicationdata.sqlite"]];
NSMigrationManager *migrator = [[NSMigrationManager alloc] initWithSourceModel:[self managedObjectModel] destinationModel:[self managedObjectModel]];
NSMappingModel *mappingModel = [NSMappingModel inferredMappingModelForSourceModel:[self managedObjectModel] destinationModel:[self managedObjectModel] error:err];
NSError **err2;
// the following line crashes when the whole block is ran twice
[migrator migrateStoreFromURL:importURL
type:NSSQLiteStoreType
options:nil
withMappingModel:mappingModel
toDestinationURL:storeURL
destinationType:NSSQLiteStoreType
destinationOptions:nil
error:err2];
NSLog(@"import finished");
[migrator release];
Solution
I see one mistake in that code right away, which has to do with the error argument of that method call. The NSError**
means that you want to give the method the address of an NSError*
, which it will use to write out a reference to an error object (if an error occurs). Right now you're just passing in an uninitialized pointer which could point to something valid or could point to total garbage, depending on what happens to be on the stack at the time. The migrator's method will write to this point, sometimes with no apparent ill effect, but sometimes resulting in a crash, like you're seeing. The code for that would look like this:
NSError *err2 = nil; //you want to initialize to nil, in case method doesn't modify your pointer at all (i.e. no error occurs)
//the method also returns a BOOL indicating success/failure
if (![migrator migrateStoreFromURL:importURL
type:NSSQLiteStoreType
options:nil
withMappingModel:mappingModel
toDestinationURL:storeURL
destinationType:NSSQLiteStoreType
destinationOptions:nil
error:&err2])
{
//handle the error
}
OTHER TIPS
Thanks Brian for pointing this out.
I ended up simply adding the additional sqlite file to the persistent stores of the stack, works very well and allows us to later limit fetchRequest to individual stores:
NSError *error = nil;
NSURL *url = SOME_FILEPATH_URL;
NSPersistentStore *newStore = [persistentStoreCoordinator
addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:url
options:opt
error:&error];
And we keep a dictionary of all persistentStores for later reference.