-
背景:
- 我们的APP只需要在用户登录的时候存储一些关键的数据,数据量不大。其余数据在线请求就行,也没有涉及大量的数据存储,用Core Data 还挺方便的
-
当我要发布企业1.2版本的时候,发现因为用了CoreData进行数据的存储,当我修改了里面的Model的时候。比如
-
当我添加或者修改一个字段或者是添加另外一个model的时候。我们会遇到两个问题
- 1.运行到真机:会报错,需要卸载APP后再运行重装
-
2.发布版本后,你无法覆盖安装而且会出错。安装不了
-
而遇上这样的问题肯定不行,用户用不了你就挂了。所以我们要做版本的
数据迁移
- 但是这种情况没有接触过就只能Google了
- 参考地址:http://jamesonquave.com/blog/core-data-migrations-swift-tutorial/
-
Why Make Core Data Migrations?
However there is a problem. Core Data expects a consistent schema. Meaning that if we add or remove entities, attributes, or change their types, it will cause a conflict. This means that if you:1) Store some data inside of an app using a set schema. For example a Model named LogItem with a title(String) attribute.2) Change any of the schema. For example add a new full title attribute to become ‘fullTitle’.3) Run the app again
There will be a conflict, and the app will do a hard crash. This includes anyone who has downloaded your app from the app store! Not only that, if you change the text attribute, and you don’t perform any kind of migration, you will be risking losing user’s data. If you want people to uninstall your app, this is a fantastic way to make that happen.- 也就是我们上面遇上的问题:当你在swift中使用了CoreData,CoreData是需要一个始终如一的模式。如果你
修改了
,添加
或者移除
了任何一个实体
,属性
或者修改了类型
。就会造成冲突
。就会导致运行
的时候你的APP崩溃
,或者别人在AppStore上下载你的APP后直接崩溃闪退,这是很严重的问题,丢失用户可能是致命问题
- 也就是我们上面遇上的问题:当你在swift中使用了CoreData,CoreData是需要一个始终如一的模式。如果你
- 创建一个
迁移Migration
--Create A Migration- 没创建迁移前Xcode会在AppDelegate中为我们自动创建下面的代码。但是Xcode并没有给出任何的
可选的迁移
。但是它有一个persistentStoreCoordinator
。它是专门负责迁移的发生。我们只需要关注options
。它是一个字典
,是一个迁移字典Migrations Options
- NSMigratePersistentStoresAutomaticallyOption sounds pretty nice. Automatic migration? Sign me up!
- NSInferMappingModelAutomaticallyOption will create the mapping model, while
- NSMigratePersistentStoresAutomaticallyOption will perform the migration.
- 没创建迁移前Xcode会在AppDelegate中为我们自动创建下面的代码。但是Xcode并没有给出任何的
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
// The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
// Create the coordinator and store
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite")
var failureReason = "There was an error creating or loading the application's saved data."
do {
try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil)
} catch {
// Report any error we got.
var dict = [String: AnyObject]()
dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
dict[NSLocalizedFailureReasonErrorKey] = failureReason
dict[NSUnderlyingErrorKey] = error as NSError
let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
// Replace this with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
abort()
}
return coordinator
}()
- 所以我们添加
let mOptions = [NSMigratePersistentStoresAutomaticallyOption: true,
NSInferMappingModelAutomaticallyOption: true]
if coordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: mOptions, error: &error) == nil {
- 创建迁移后是:
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
// The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
// Create the coordinator and store
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite")
var failureReason = "There was an error creating or loading the application's saved data."
do {
let mOptions = [NSMigratePersistentStoresAutomaticallyOption: true,
NSInferMappingModelAutomaticallyOption: true]
try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: mOptions)
} catch {
// Report any error we got.
var dict = [String: AnyObject]()
dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
dict[NSLocalizedFailureReasonErrorKey] = failureReason
dict[NSUnderlyingErrorKey] = error as NSError
let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
// Replace this with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
abort()
}
return coordinator
}()
- 也就是说,我们添加了:
let mOptions = [NSMigratePersistentStoresAutomaticallyOption: true,
NSInferMappingModelAutomaticallyOption: true]
try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: mOptions)
这样基础的操作,是让我们的Core Data去尝试让我们的Models在不同的版本之间自动合并以及映射
但是这样还是不够,我们还需要接下来的操作
添加一个Model版本--Add a Model Version
先要选上你的Core Data model,然后在工程的导航栏部分-->Editor-->Add Model Version(不选上,不会出现这个)
- 名字可以随便起,但是建议最好叫xxxV2/3/...便于区分,因为你每迁移或者增加Model一次就产生了一次新的Model Schema.每次都会变的哦
- 然后我们就要Model Virsion2成为我们的可用的Model.选中Core Data model。然后:
这样你就可以在MedelV2中修改你想修改的了,APP运行和发布后也就不会出错了
-
重要的几点:
- 1.设置好options
- 2.添加新的Model Virsion
- 3.选好新的Model Virsion
-
猜测的几点
- 1.每次大的修改Model都要添加新的Model Virsion.版本不停往上
- 2.修改哪怕是退回去的时候,也最好是新建一个新的Model Virsion,删除不想要的
- 3.有些还需要我去继续验证
- 在项目中遇上了一个Swift中的一个坑:当APP发版后,有用户发现他的iPhone5和5c装上APP点击登录后直接崩溃闪退。其余的手机没有问题。首先我以为是我们SDK支持的架构不支持ARMv7s,但是问清楚后发现是支持4s往上的。所以问题肯定出现在自己的程序中。查看崩溃日志(查看崩溃日志的工具Log Guru:http://club.fir.im/topic/54daf35374c4c85e73e4aaba)。
发现是自己有一个必须用到的参数id的问题
- 这里的问题是:Swift是强语言,类型要求十分严格。我在Core Data里用了一个Integer64,然后Swift代码里面写的是Int。最后导致程序不能安装(iPhone5和5c的CPU是32位的,所以要用Int32向上兼容。这里就用到了上面数据的迁移将它改成Int32,也不会出问题了)如果是已经上架AppStore的话,5和5c用户就等下一个版本吧。。。我也就分分钟去财务了