PeterK
PeterK

Reputation: 4301

Update read-only Core Data sqlite in main bundle

I am using a read-only Core Data sqlite from the Main Bundle, works well. When i add a new version of the database (more read-only data) to the main bundle it still reads the "Old" version of the database.

Anyone that can help me understand why and what to do to get the new database version the current one when a current user download an update with the new version of the database?

This is part of trying to solve the problem in this post: Same problem when accessing updated database from documents directory

===SOLUTION====

I solved this by changing the name of the new database in the "new" main bundle and it works like a dream. Also, if this is an update i delete the old database in the documents directory to clean up.

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {

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


//===READ DATABASE FROM MAIN BUNDLE===//
NSFileManager *fileManager = [NSFileManager defaultManager];

NSURL *storeUrl = [[NSBundle mainBundle] URLForResource:kNewDB withExtension:@"sqlite"];

//=== IF THE OLD DATABASE STILL EXIST DELETE IT FROM DOCUMENT DIRECTORY ===//
NSURL *oldDatabasePathURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"database.sqlite"];
NSString *oldDatabasePath = [oldDatabasePathURL path];
if ([fileManager fileExistsAtPath:oldDatabasePath]) {
    //Remove old database from Documents Directory
    [fileManager removeItemAtURL:oldDatabasePathURL error:nil];

}

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

persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];

NSError *error;
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]) {
    // Update to handle the error appropriately.
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    exit(-1);  // Fail
}    

return persistentStoreCoordinator;
}

Upvotes: 2

Views: 1173

Answers (2)

Mike
Mike

Reputation: 791

PeterK, I was having the same issue when using the tutorial at http://www.raywenderlich.com/12170/core-data-tutorial-how-to-preloadimport-existing-data-updated to use a read-only sqlite database by Core Data. All was fine, until I had to update my database and re-release my target app. As you know, the proposed code in that tutorial only copies in the new database if no database exists in the Application's Documents directory.

I did not think that renaming my database (and updating the copying code) was a good design approach, so I got my design working by following Inafziger's preferred advise and reading up on iOS file structure. I provide the below only to show how to implement Inafziger's proposal. And of note is that this approach likely only works if your app does not change the contents of the Core Data information as it is read in as read-only.

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
  if (_persistentStoreCoordinator != nil) {
    return _persistentStoreCoordinator;
  }    

  // Updated processing to now just have the NRPersistentStoreCoordinator point to the sqlite db in the
  //           application's Bundle and not by copying that db to the app's Documents directory and then using
  //           the db in the Documents directory.
  NSURL *refactoredStoreURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@“NameOfYourDatabase ofType:@"sqlite"]];


  NSError *error = nil;

  _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];

  // Added to ensure the NSPersistentStoreCoordinator reads the Bundle's db file as read-only since
    //           it is not appropriate to allow the app to modify anything in the Bundle
  NSDictionary *readOnlyOptions = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSReadOnlyPersistentStoreOption, nil];

  // Use the URL that points to the Bundle's db file and used the ReadOnly options
  if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:refactoredStoreURL options:readOnlyOptions error:&error]) {
    // Your logic if there is an error
  }
  return _persistentStoreCoordinator;
}   

I hope this helps the next reader of this question.

Upvotes: 1

lnafziger
lnafziger

Reputation: 25740

You must have a place in your code where you check to see if a copy of the database file exists in some writable directory (possibly your Documents directory) and if not, then you copy it there. This is a very common approach to take when you need to make changes to your database. The problem is, when you update your app, the file already exists, so it is never copied over again.

There are two approaches to take to fix your problem:

  1. (Preferable): Don't copy the database in the first place. Since it is read only, you don't need to, and it just takes up extra space on the device. Simply open the database using the path of the file that is in the main bundle.

  2. Instead of checking to see if a file exists in the writable directory, check to see if it is newer than the one in the main bundle. (not by using the date, since they could have installed the program and created the file after your update was submitted to the app store for approval, which would result in the new one not being copied over. You need to check the version of the database, possibly by storing another file in your app bundle which stores the version info, or determining it with version specific code). If not, then copy it over again.

Upvotes: 2

Related Questions