iOS7.1 现有应用核心数据更新失败
Posted
技术标签:
【中文标题】iOS7.1 现有应用核心数据更新失败【英文标题】:Core data update of existing app fails on iOS7.1 【发布时间】:2014-08-01 15:03:48 【问题描述】:我在 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 时,我收到以下错误。
未解决的错误 Error Domain=NSCocoaErrorDomain Code=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】:这里的错误信息很能说明问题。它说:
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 文件之间的不匹配,但后来发现该错误更多地与加密解密过程中的密钥不匹配有关。这是我的第一个猜测,我曾要求我的同事检查这一点,但他以某种方式验证了这一点,我们不赞成这个理论。
我们的加密工作是通过将运行时生成的加密密钥的一部分保存到钥匙串中来实现的。现在前提是app升级前和升级后keychain中的值保持不变。但是由于使用了错误的 Bundle 标识符,升级后的应用没有从钥匙串中获取值,而是创建了自己的运行时值。
仅当完整的捆绑包标识符相同时,存储在钥匙串中的值才会在应用程序和应用程序更新之间共享。
app1 的包标识符为 BundleIdentifierPrefix1.com.yourcompany.appname app1.1 的包标识符为 BundleIdentifierPrefix2.com.yourcompany.appname
由于反向域相同,因此应用程序作为更新安装,但由于前缀不同,因此未共享密钥链值。
【讨论】:
以上是关于iOS7.1 现有应用核心数据更新失败的主要内容,如果未能解决你的问题,请参考以下文章
核心数据:NSManagedObjectContext 未保存/获取请求失败,直到应用退出和重新启动
NSPersistentStoreCoordinator 在 ios iOS 中没有持久存储(架构不匹配或迁移失败)核心数据