iOS7.1上现有应用的核心数据更新失败

时间:2014-06-11 14:00:12

标签: sqlite core-data keychain ios7.1 core-data-migration

我在AppStore上有一个应用程序,它是在12月13日使用Xcode 4.3提交的,它有一个核心数据模型(版本2)。我现在要发布更新核心数据模型的下一个版本。当我在调试模式下运行代码时,迁移工作正常。但是,当我通过TestFlight进行发布时,迁移失败,我得到以下错误。出于安全原因,我每次退出应用程序时都会删除数据库(我保存加密的副本),并在下次启动时解密此数据库。

用于初始化PersistentStoreCoordinator的代码。

NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSDictionary *options = @{
                          NSMigratePersistentStoresAutomaticallyOption : @YES,
                          NSInferMappingModelAutomaticallyOption : @YES
                          };


// Check if we need a migration
NSDictionary *sourceMetadata = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:NSSQLiteStoreType URL:storeURL error:&error];
NSManagedObjectModel *destinationModel = [_persistentStoreCoordinator managedObjectModel];
BOOL isModelCompatible = (sourceMetadata == nil) || [destinationModel isConfiguration:nil compatibleWithStoreMetadata:sourceMetadata];
if (! isModelCompatible) {
    // We need a migration, so we set the journal_mode to DELETE
    options = @{NSMigratePersistentStoresAutomaticallyOption:@YES,
                NSInferMappingModelAutomaticallyOption:@YES,
                NSSQLitePragmasOption: @{@"journal_mode": @"DELETE"}
                };
}

NSPersistentStore *persistentStore = [_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error];
if (! persistentStore) {


    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documents = [paths objectAtIndex:0];
    NSString *databasePath = [documents stringByAppendingPathComponent:@"Store"];
    NSString *sqlite = [databasePath stringByAppendingPathComponent:@"myDatabase.sqlite"];

    [[NSFileManager defaultManager] removeItemAtPath:sqlite error:nil];

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

// Reinstate the WAL journal_mode
if (! isModelCompatible) {
    [_persistentStoreCoordinator removePersistentStore:persistentStore error:NULL];
    options = @{NSMigratePersistentStoresAutomaticallyOption:@YES,
                NSInferMappingModelAutomaticallyOption:@YES,
                NSSQLitePragmasOption: @{@"journal_mode": @"WAL"}
                };
    [_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error];
}

return _persistentStoreCoordinator;

当我尝试初始化persistentStoreCoordinator时,我收到了波纹管错误。

  

未解决的错误错误域= NSCocoaErrorDomain代码= 259“操作无法完成。(Cocoa错误259.)”UserInfo = 0x15df4dc0 {NSUnderlyingException =路径中的文件似乎不是SQLite数据库:/ var / mobile /Applications/9B623099-5591-4C55-BA83-77A057B94690/Documents/Store/myDatabase.sqlite},{

NSUnderlyingException = "File at path does not appear to be a SQLite database: /var/mobile/Applications/9B623099-5591-4C55-BA83-77A057B94690/Documents/Store/myDatabase.sqlite";}

奇怪的是,在iOS7.0.6上,升级方案在Dev和Release配置上都运行良好,但在iOS7.1上它似乎只适用于Dev配置。我已经厌倦了删除WAL和SHM文件,但无济于事。

1 个答案:

答案 0 :(得分:1)

错误信息在这里很有说服力。它说:

NSUnderlyingException = "File at path does not appear to be a SQLite database: /var/mobile/Applications/9B623099-5591-4C55-BA83-77A057B94690/Documents/Store/myDatabase.sqlite";}

我认为这可能是由于更新的核心数据sqlite与WAL文件不匹配,但事后证明错误更多与加密 - 解密过程中的密钥不匹配有关。这是我的第一个猜测,我曾要求我的同事检查这个,但他以某种方式验证了这一点,我们不赞成这个理论。

我们的加密工作方法是将部分运行时生成的加密密钥保存到钥匙串中。现在的前提是,在升级之前和升级之后钥匙串的价值仍然存在。但是由于使用了错误的Bundle标识符,升级的应用程序没有从keychain获取值,并且它创建了自己的运行时值。

只有完整的包标识符相同时,才会在应用和应用更新之间共享存储在钥匙串中的值。

app1的包标识符为 BundleIdentifierPrefix1 .com.yourcompany.appname app1.1的包标识符为 BundleIdentifierPrefix2 .com.yourcompany.appname

由于反向域相同,因此应用程序作为更新安装,但由于前缀不同,因此不会共享密钥链值。