How to use a provided store file with Core Data

Apple’s recommended method for dealing with “bring your own store files” for Core Data is to copy the store file into your app’s Documents directory, where it can be accessed for read and write queries.

However, if you don’t need to write to your store file, then you can also add a provided store file inside the main bundle. It would save some space on the user’s device.

Here’s the custom initialiser for NSPersistentStoreCoordinator, tweaked to show how to handle this:

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    if (_persistentStoreCoordinator != nil) {
        return _persistentStoreCoordinator;
    }
    
    NSURL *providedStore = [[NSBundle mainBundle] URLForResource:@"YourStorefile" withExtension:@"sqlstore"];
    NSFileManager *manager = [NSFileManager defaultManager];
    
    // if we don't have a file in our bundle, freak out at once!
    if (![manager fileExistsAtPath:[providedStore path]]) {
        NSLog(@"Houston, we have a problem: The provided store file doesn't exist in our bundle :-(");
        abort();
    }
    
    NSError *error = nil;
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    
    // the options is nil by default
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:providedStore options:@{NSReadOnlyPersistentStoreOption:@YES} error:&error]) {

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

Note that the option NSReadOnlyPersistentStoreOption:@YES is not strictly necessary.





2 thoughts on “How to use a provided store file with Core Data

  1. For completion: if you DO want to copy a provided store file and follow Apple’s guidelines, here’s how to do it. This is the same custom initialiser as above, but with copy/delete routine:

    - (NSPersistentStoreCoordinator *)persistentStoreCoordinator
    {
        if (_persistentStoreCoordinator != nil) {
            return _persistentStoreCoordinator;
        }
        
        NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"AppStore.sqlstore"];
        NSURL *providedStore = [[NSBundle mainBundle] URLForResource:@"YourStoreFile" withExtension:@"sqlstore"];
        NSFileManager *manager = [NSFileManager defaultManager];
        
        // if we don't have a file in our bundle, freak out at once!
        if (![manager fileExistsAtPath:[providedStore path]]) {
            NSLog(@"Houston, we have a problem: The provided store file doesn't exist in our bundle :-(");
            abort();
        }
        
        // if there's a file in Documents, delete it first
        if ([manager fileExistsAtPath:[storeURL path]]) {
            
            NSError *error = nil;
            if (![manager removeItemAtURL:storeURL error:&error]) {
                NSLog(@"Couldn't delete the existing store file.");
            }
        }
        
        // copy our own store file to the right location
        NSError *fileError = nil;
        if (![manager copyItemAtURL:providedStore toURL:storeURL error:&fileError]) {
            NSLog(@"Houston, we have a problem!\nCouldn't copy the provided store file: %@", fileError);
            abort();
        }
        
        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;
    }
    
  2. Note that on iOS 7 the SQLite Engine likes to write two additional files: .wal and .shm. While the latter appears to be completely undocumented, .wal files are temporary files used by SQLite which speed up read/write queries. They *should* be deleted at the end of a session, but it appears that they are not on iOS 7.

    Some versions may complain when those files are not present, causing an error message such as “The Disk Image is malformed”. If this happens, just opt to copy the store file instead.

Leave a Reply