1

I am using a read only sqlite database located in MainBundle for my app. In the PersistentStoreCoordinator I am loading the database with the following code:

    NSDictionary *storeOptions = @{NSReadOnlyPersistentStoreOption : [NSNumber numberWithBool:YES]};

NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self applicationManagedObjectModel]];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[[NSBundle mainBundle] URLForResource:@"applications" withExtension:@"sqlite"] options:nil error:&error]) {
    NSLog(@"Unresolved error %@, %@", error, [error description]);
    abort();
}

In iOS7 this code crashing both in the simulator, and the device with the following error:

 CoreData: error: (14) I/O error for database at /var/mobile/Applications/4B81AEFE-03E6-4156-B52D-3452515FACAF/myapp.app/applications.sqlite.  SQLite error code:14, 'unable to open database file'
2014-03-22 20:45:34.346 GfxHotkeys3[1369:60b] CoreData: error: Encountered exception I/O error for database at /var/mobile/Applications/4B81AEFE-03E6-4156-B52D-3452515FACAF/myapp.app/applications.sqlite.  SQLite error code:14, 'unable to open database file' with userInfo {
    NSFilePath = "/var/mobile/Applications/4B81AEFE-03E6-4156-B52D-3452515FACAF/myapp.app/applications.sqlite";
    NSSQLiteErrorDomain = 14;
} while checking table name from store: <NSSQLiteConnection: 0x155b0fd0>
2014-03-22 20:45:34.372 GfxHotkeys3[1369:60b] Unresolved error Error Domain=NSCocoaErrorDomain Code=256 "The operation couldn’t be completed. (Cocoa error 256.)" UserInfo=0x155b01b0 {NSUnderlyingException=I/O error for database at /var/mobile/Applications/4B81AEFE-03E6-4156-B52D-3452515FACAF/myapp.app/applications.sqlite.  SQLite error code:14, 'unable to open database file', NSSQLiteErrorDomain=14}, Error Domain=NSCocoaErrorDomain Code=256 "The operation couldn’t be completed. (Cocoa error 256.)" UserInfo=0x155b01b0 {NSUnderlyingException=I/O error for database at /var/mobile/Applications/4B81AEFE-03E6-4156-B52D-3452515FACAF/GfxHotkeys3.app/applications.sqlite.  SQLite error code:14, 'unable to open database file', NSSQLiteErrorDomain=14}

passing nil in store options, will run the app in the simulator but not on the device (since it can't write on MainBundle).

I can solve this by copying the database into documents and loading from there, but I am wondering why its happening. I am using this option for a number of years now for loading read only sqlite from MainBundle, but in iOS 7 is crashing...

Any clues?

Many Thanks

Nimrod7
  • 1,425
  • 2
  • 17
  • 30

4 Answers4

3

The issue is that the original file was created with the WAL option (that is now default on OS X Mavericks and iOS 7). To be able to use the NSReadOnlyPersistentStoreOption, you need to create a file that has WAL turned off. You need to do add this directive to the iOS project:

NSDictionary *storeOptions = @{NSReadOnlyPersistentStoreOption : [NSNumber numberWithBool:YES], NSSQLitePragmasOption : @{@"journal_mode" : @"DELETE"}}

As well as in the project that you create the original file with. E.i. reimport your data into a NSPersistantStore file created with the NSSQLitePragmasOption set with:

NSDictionary *storeOptions = @{NSSQLitePragmasOption : @{@"journal_mode" : @"DELETE"}};

I consider this a bug in the implementation as there is no technical reason for this error, the file is not loaded simply due to the way that Core Data handles the metadata on the file that was created with WAL.

Conor
  • 1,781
  • 17
  • 27
2

To open a read-only store, you should use

NSDictionary *storeOptions = @{NSReadOnlyPersistentStoreOption : @YES};

instead of @NO as you did.

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • typo in the example above. Thanks for pointing it out. Fixed to avoid confusion. The code crashes on the device with NSReadOnlyPersistentStoreOption @YES – Nimrod7 Mar 22 '14 at 19:24
  • @Nimrod7: Is the file name correct? Note that the file system on the device is case *sensitive*, so "applications.sqlite" and "Applications.sqlite" would be different files. - Does it help if you disable the WAL-mode, as described here: http://stackoverflow.com/a/20082157/1187415? – Martin R Mar 22 '14 at 19:37
  • file is in correct case. Disabling WAL-mode does not help. crashing both in device and simulator – Nimrod7 Mar 22 '14 at 20:44
  • @Nimrod7: I just found this http://stackoverflow.com/questions/18773466/coredata-error-14-i-o-error-for-database, which looks very similar to your problem (and I also gave a wrong answer :-). Note that disabling WAL-mode was the solution there. – Martin R Mar 22 '14 at 20:52
  • The issue looks the same. The database from iOS 6 works, so I guess I have to use iOS 6 simulator and disable WAL mode, then import the db to iOS7 project. I am marking it as the correct answer, its the most complete explanation on what's going on. Thanks for pointing to this. – Nimrod7 Mar 22 '14 at 21:00
2

I think you can easily resolve the problem by first copying the store to the documents directory. You can then pass the read-only option or not (it does not matter, you could just not write to it).

At the least that could be a good test to see if you can isolate the issue..

I think the reason for the crash could be because the new version of SQLite creates another two files when the store is accessed. Because the bundle is not writable, you get the crash.

Mundi
  • 79,884
  • 17
  • 117
  • 140
0

In swift 3.0, initialize your persistence store coordinator with the following:

fileprivate lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
    var coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)

    let bundlePath = Bundle.main.path(forResource: "TrackWaypoints", ofType: ".sqlite")
    let bundleURL = URL(fileURLWithPath: bundlePath!)

    let hi = [NSReadOnlyPersistentStoreOption:true, NSSQLitePragmasOption: ["journal_mode": "delete"]] as [String: Any]
    let store = try! coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: bundleURL, options: hi)
    return coordinator
}()
Harry Netzer
  • 312
  • 1
  • 2
  • 17