views:

185

answers:

2

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];
A: 

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
}
Brian Webster
A: 

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.

Felix