0

Clarification on 'not accessing' sqlite DB: The context/store is not able to detect any entries in the .sqlite on devices only, despite the NSBundle having the .sqlite and the file size of the .sqlite being the correct size. In the simulator, the context/store works fine.

I'm pretty stumped on this. I added a model version to my xcdatamodeld. I reset the version, have my code to automatically do migrations, and repopulated my database.

On the iPhone simulator, it works. However, when running off a device, it fails to read from the database.

I've put in code to detect that the sqllite is actually on the device and also verify the file size. The SQLLite is being copied over correctly from the NSBundle, as well as the file size indicating there is data.

Somehow, my code is refusing to access the DB on the device. Any idea why this is? The code for accessing the DB has not changed - the only thing that changed was the datamodel. The code below was working for devices prior to the upgrade.

// Returns the managed object context for the application.
// If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
- (NSManagedObjectContext *)managedObjectContext
{
if (_managedObjectContext != nil) {
    return _managedObjectContext;
}

NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
    _managedObjectContext = [[NSManagedObjectContext alloc] init];
    [_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
}

// Returns the managed object model for the application.
// If the model doesn't already exist, it is created from the application's model.
- (NSManagedObjectModel *)managedObjectModel
{
  if (_managedObjectModel != nil) {
    return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"sirona" withExtension:@"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}

// Returns the persistent store coordinator for the application.
// If the coordinator doesn't already exist, it is created and the application's store added to it.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (_persistentStoreCoordinator != nil) {
    return _persistentStoreCoordinator;
}

NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"sirona.sqlite"];

NSString *databasePath = [NSBundle.mainBundle pathForResource:@"sirona" ofType:@"sqlite"];
NSLog(@"File manager: %@", [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[[NSBundle mainBundle] bundlePath] error:nil]);
NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath: @"sirona.sqlite" error: NULL];
UInt32 result = [attrs fileSize];
NSLog(@"File size: %d", (unsigned int)result);
NSError *error = nil;

// important part starts here
   NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                            [NSNumber numberWithBool:YES],NSMigratePersistentStoresAutomaticallyOption,
                            [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption,
                                                           nil];

_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    abort();
}

return _persistentStoreCoordinator;
}

How I'm accessing the DB through core data:

  NSFetchRequest *request = [[NSFetchRequest alloc] init];
  [request setEntity:[NSEntityDescription entityForName:@"Representative" inManagedObjectContext:context]];
  [request setIncludesSubentities:NO]; //Omit subentities. Default is YES (i.e. include subentities)

  NSError *err;
  NSUInteger count = [context countForFetchRequest:request error:&err];
  NSLog(@"Count: %lu", (unsigned long)count);

Edit: Not sure how this was working without the code to copy it to the device. But it seems to be working now.

Code to do this:

NSURL *storeURL = [[self applicationDocumentsDirectory]    URLByAppendingPathComponent:@"sirona.sqlite"];

if (![[NSFileManager defaultManager] fileExistsAtPath:[storeURL path]]) {
    NSURL *preloadURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"sirona" ofType:@"sqlite"]];
    NSLog(@"PreloadURL: %@", preloadURL);
    NSError* err = nil;

    if (![[NSFileManager defaultManager] copyItemAtURL:preloadURL toURL:storeURL error:&err]) {
        NSLog(@"Oops, could copy preloaded data");
    }
}

NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
LyricalPanda
  • 1,424
  • 14
  • 25
  • Which part is actually failing? Are some of those `NSLog`s printing something different than you expect? "refusing to access" is pretty vague. – Tom Harrington Jun 26 '13 at 16:37
  • Sorry - I'll update the question a bit more. The part that is failing is actually getting data from the .sqlite - specifically, it's showing 0 results for any Entries I search for within the DB only on the device (the same code works for the simulator). I don't get any crashes and NSLogs are validating that the .sqlite is there (and has data within it). It was me checking to make sure the .sqlite is getting bundled when archived. – LyricalPanda Jun 26 '13 at 16:40
  • I do not see the `[_persistentStoreCoordinator addPersistentStoreWithType:...]` call in your code where the sqlite file at `storeURL` is attached to the store coordinator! (But if that code were really missing than it would fail on the simulator as well :-) – Martin R Jun 26 '13 at 17:01
  • Ah! Sorry Martin I accidentally deleted it out of the copy and paste when formatting it. Good eye though! Added it into the copy and paste above - it is in the code though :). – LyricalPanda Jun 26 '13 at 17:06
  • @LyricalPanda: Please try always to copy/paste real and complete code. You have `_persistentStoreCoordinator` at one place and `_persistantStoreCoordinator` at another place. That makes it unnecessary complicated for others to understand the code and find the problem! – Martin R Jun 26 '13 at 17:10
  • @LyricalPanda: I cannot see in your code where you copy the database from the bundle into the documents directory. And the file size that you print is *not* from the file in the documents directory. – Martin R Jun 26 '13 at 17:37
  • @MartinR Interesting, That's actually all the code that I have. If I'm missing code, I wonder why it was working prior to the datamodel upgrade. I'll try to find how to copy the database from the bundle into the documents directory – LyricalPanda Jun 26 '13 at 17:52
  • @MartinR Yep. Not sure how this was working before when I was missing that code on devices. I'll edit my comment with the code that seems to have fixed it – LyricalPanda Jun 26 '13 at 18:23

1 Answers1

0

You must copy your sqlite file on a folder where you hare read/write permissions, before initialize the CoreData context

Manu
  • 788
  • 5
  • 10
  • In managedObjectContext, I'm creating coordinator before alloc and initializing the ObjectContext. So it looks like the sqlite file is being copied before the context is created. How can i check though if the folder it's being written to has read/write permissions? – LyricalPanda Jun 26 '13 at 16:38
  • looking at your code you still refer to the file into the MainBundle... so your sqlite file is a readonly file, you shoud copy the file manually into you NSHomeDirectory()/Ducuments ... or something like that – Manu Jun 26 '13 at 17:55