Updating sqlite file for Core Data app issues

Discussion in 'iOS Programming' started by ace2600, Oct 9, 2009.

  1. ace2600 macrumors member

    Joined:
    Mar 16, 2008
    Location:
    Austin, Texas
    #1
    Hi,

    I'm creating an SQLite file in a Mac app using Core Data and then placing it in my iPhone app. The SQLite file is read-only, it's just mappings for states and cities. I added more cities to the file and am trying to get my iPhone app to read in the new SQLite file and use that as its peristent store.

    I was hoping just replacing the SQLite file in the app would work, but it does not.

    The steps I've tried are: I run the app in simulator with the old sqlite file. Exit. Replace the sqlite file with the new one in xcode. Add NSMigratePersistentStoresAutomaticallyOption and NSInferMappingModelAutomaticallyOption to the addPeristentStore options. Then run the app again, but the app only shows the same data from the original file.

    I've also tried versioning the model as per the Apple docs and renaming the sqlite file. But still no luck (I'm guessing because the model is still the same?).

    Any ideas on how I can have the app read the newer sqlite file?

    My iPhone app code:
    PHP:
    - (NSManagedObjectModel *)geographyObjectModel
    {
        if (
    geographyObjectModel_ == nil)
        {
            
    NSString *modelPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"Geography" ofType:@"mom"];
            
    NSManagedObjectModel *managedObjectModel = [[NSManagedObjectModel allocinitWithContentsOfURL:[NSURL fileURLWithPath:modelPath]];
            [
    self setGeographyObjectModel:managedObjectModel];
            [
    managedObjectModel release];
        }
        
        return 
    geographyObjectModel_;
    }

    //Geographical data is stored in its own persistent storage
    - (NSPersistentStoreCoordinator *)geographyStoreCoordinator
    {
        if (
    geographyStoreCoordinator_ == nil
        {    
            
    NSString *storePath = [[AppDelegate applicationDocumentsDirectorystringByAppendingPathComponent:@"Geography.sqlite"];
            
            
    // Check to see if the store already exists
            
    NSFileManager *fileManager = [NSFileManager defaultManager];
            
    // Copy the default store if necessary
            
    if (![fileManager fileExistsAtPath:storePath]) 
            {
                
    NSString *defaultStorePath = [[NSBundle mainBundlepathForResource:@"Geography" ofType:@"sqlite"];
                if (
    defaultStorePath
                {
                    [
    fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
                }
            }
            
            
    NSPersistentStoreCoordinator *persistentStoreCoordinator = [[NSPersistentStoreCoordinator allocinitWithManagedObjectModel:[self geographyObjectModel]];
            [
    self setGeographyStoreCoordinator:persistentStoreCoordinator];
            [
    persistentStoreCoordinator release];
            
            
    NSMutableDictionary *options nil;
            
    //Implement options when updating the model. Then comment options back out.
            
    options = [NSMutableDictionary dictionary];
            
    //Uncomment the line below to ignore version hash checks
            //[options setObject:[NSNumber numberWithBool:YES] forKey:NSIgnorePersistentStoreVersioningOption];
            //When migrating, uncomment the setObject lines belows
            
    [options setObject:[NSNumber numberWithBool:YESforKey:NSMigratePersistentStoresAutomaticallyOption];
            [
    options setObject:[NSNumber numberWithBool:YESforKey:NSInferMappingModelAutomaticallyOption];        
            
    NSURL *storeUrl = [NSURL fileURLWithPath:storePath];
            
    NSError *error;
            if (![[
    self geographyStoreCoordinatoraddPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error])
            {
                
    // Handle the error.
                
    DebugLog(@"error: %@"error);
            }
        }
        
        return 
    geographyStoreCoordinator_;
    }
    If interested, here is the GitHub code for exporting the state - city mappings in Core Data: http://github.com/baalexander/Geography-Importer.
     
  2. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #2
    Did you do a clean or delete the build folder?
     
  3. ace2600 thread starter macrumors member

    Joined:
    Mar 16, 2008
    Location:
    Austin, Texas
    #3
    I cleaned all targets and deleted the build folder. I did not delete the app from the simulator because I'm trying to test updating.
     
  4. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #4
    OK, if you look inside your app in the build folder what do you find? It's not possible to open a file that isn't present on the device. Also, you should check the error returned from copyItemAtPath:topath:
     
  5. ace2600 thread starter macrumors member

    Joined:
    Mar 16, 2008
    Location:
    Austin, Texas
    #5
    Thank you for the continued help PhoneyDeveloper.

    Your point about copyItemAtPath:topath: forced me to reread that code. It seems obvious now that replacing my old Geography.sqlite file with one of the exact same name won't work as the condition "if (![fileManager fileExistsAtPath:storePath]) " won't occur.

    I got it to work by adding the SQLite file as Geography2.sqlite and adding the parameter NSIgnorePersistentStoreVersioningOption (working code at the end).

    I hope someone can still explain why NSIgnorePersistentStoreVersioningOption is needed. My guess is because the version/hash values are different because of the data, even though the model is the exact same.

    Also, I am quite curious if there is a better way to go about the task of updating the read-only SQLite file than my steps above. I tried versioning the model instead of using this NSIgnorePersistentStoreVersioningOption parameter by clicking on the xcdatemodel file then Design -> Add Model Version then Design -> Set Current Version, but no luck. Also, NSMigratePersistentStoresAutomaticallyOption and NSInferMappingModelAutomaticallyOption never helped for this issue.

    New working code:
    PHP:
    - (NSManagedObjectModel *)geographyObjectModel
    {
        if (
    geographyObjectModel_ == nil)
        {
            
    NSString *modelPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"Geography" ofType:@"mom"];
            
    NSManagedObjectModel *managedObjectModel = [[NSManagedObjectModel allocinitWithContentsOfURL:[NSURL fileURLWithPath:modelPath]];
            [
    self setGeographyObjectModel:managedObjectModel];
            [
    managedObjectModel release];
        }
        
        return 
    geographyObjectModel_;
    }

    //Geographical data is stored in its own persistent storage
    - (NSPersistentStoreCoordinator *)geographyStoreCoordinator
    {
        if (
    geographyStoreCoordinator_ == nil
        {    
            
    NSString *storePath = [[AppDelegate applicationDocumentsDirectorystringByAppendingPathComponent:@"Geography2.sqlite"];
            
            
    // Check to see if the store already exists
            
    NSFileManager *fileManager = [NSFileManager defaultManager];
            
    // Copy the default store if necessary
            
    if (![fileManager fileExistsAtPath:storePath]) 
            {
                
    NSString *defaultStorePath = [[NSBundle mainBundlepathForResource:@"Geography2" ofType:@"sqlite"];
                if (
    defaultStorePath
                {
                    [
    fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
                }
            }
            
            
    NSPersistentStoreCoordinator *persistentStoreCoordinator = [[NSPersistentStoreCoordinator allocinitWithManagedObjectModel:[self geographyObjectModel]];
            [
    self setGeographyStoreCoordinator:persistentStoreCoordinator];
            [
    persistentStoreCoordinator release];
            
            
    NSMutableDictionary *options nil;
            
    //Implement options when updating the model. Then comment options back out.
            
    options = [NSMutableDictionary dictionary];
            
    //Uncomment the line below to ignore version hash checks
            
    [options setObject:[NSNumber numberWithBool:YESforKey:NSIgnorePersistentStoreVersioningOption];
            
    //When migrating, uncomment the setObject lines belows
            //[options setObject:[NSNumber numberWithBool:YES] forKey:NSMigratePersistentStoresAutomaticallyOption];
            //[options setObject:[NSNumber numberWithBool:YES] forKey:NSInferMappingModelAutomaticallyOption];        
            
    NSURL *storeUrl = [NSURL fileURLWithPath:storePath];
            
    NSError *error;
            if (![[
    self geographyStoreCoordinatoraddPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error])
            {
                
    // Handle the error.
                
    DebugLog(@"error: %@"error);
            }
        }
        
        return 
    geographyStoreCoordinator_;
    }
     
  6. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #6
    If the database is strictly read-only why can't you just read the file from the app bundle?
     
  7. ace2600 thread starter macrumors member

    Joined:
    Mar 16, 2008
    Location:
    Austin, Texas
    #7
    I'm not entirely clear on your question PhoneyDeveloper.

    If you're asking why I use the code below for my object model instantiation:
    PHP:
    - (NSManagedObjectModel *)geographyObjectModel
    {
        if (
    geographyObjectModel_ == nil)
        {
            
    NSString *modelPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"Geography" ofType:@"mom"];
            
    NSManagedObjectModel *managedObjectModel = [[NSManagedObjectModel allocinitWithContentsOfURL:[NSURL fileURLWithPath:modelPath]];
            [
    self setGeographyObjectModel:managedObjectModel];
            [
    managedObjectModel release];
        }
        
        return 
    geographyObjectModel_;
    }
    instead of:
    PHP:
    - (NSManagedObjectModel *)geographyObjectModel
    {
        if (
    geographyObjectModel_ == nil)
        {
            [
    self setGeographyObjectModel:[NSManagedObjectModel mergedModelFromBundles:nil]];
        }

        return 
    geographyObjectModel_;
    }
    it's because I have multiple models and found the first way easier to deal with them. If you meant something different, could you explain further?
     

Share This Page