Swift - Coredata Migration - 根据旧属性值设置新属性值

Sha*_*awn 11 core-data ios swift

我当前的核心数据实体之一 - Entity1 - 具有一个Boolean名为的属性isSaved.

在新的核心数据模型中,我计划删除isSaved属性并添加一个Int名为的新属性type.对于所有保存的Entity1对象,我想type根据isSaved旧核心数据模型中的值设置值.(例如,如果isSaved为true,则type为1,else type为2).

我已经阅读了一些关于轻量级核心数据迁移的文章,但它们似乎都没有用.

只是想知道是否有任何方法可以使我计划的迁移工作?

Tom*_*ton 39

轻量级迁移无法做到这一点.你必须创建一个映射模型和一个子类NSEntityMigrationPolicy.对于大多数iOS开发人员而言,这并不困难,但这是一个陌生的领域.步骤如下:

  1. 创建映射模型.在Xcode中,文件 - >新建 - >映射模型.单击"下一步"时,Xcode将询问此映射的源(旧)和目标(新)模型文件.
  2. 模型文件将尽可能推断映射.其他一切都是空白的.使用您type和其他一些属性,它看起来像下面这样.条目类似于$source.timestamp在迁移之前复制现有值.

初始映射

  1. 创建一个新的子类NSEntityMigrationPolicy.给子类一个明显的名称,如ModelMigration1to2.该类将告诉Core Data如何将旧布尔值映射到新的整数值.

  2. 向子类添加方法以转换值.像下面这样的东西.方法名称并不重要,但如果您选择描述性的内容则很好.你需要在这里使用ObjC类型 - 例如NSNumber代替IntBool.

    func typeFor(isSaved:NSNumber) -> NSNumber {
        if isSaved.boolValue {
            return NSNumber(integerLiteral: 1)
        } else {
            return NSNumber(integerLiteral: 2)
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 返回映射模型并告诉它使用您的子类作为其自定义映射策略.那是在"自定义政策"下右侧的检查员.请务必包含模块名称和类名.

自定义政策

  1. 告诉映射模型使用您之前创建的函数type从旧isSaved属性获取属性的值.以下说明使用一个参数调用名为typeForIsSaved:(:重要)的自定义策略类的函数,并且该参数应该是(旧的托管对象)isSaved上的值$source.

自定义属性映射

迁移现在应该有效.您不必告诉Core Data使用映射模型 - 它将确定需要迁移并查找与旧模型和新模型版本匹配的模型.

几个笔记:

  • 如果你碰到了类似的错误,Couldn't create mapping policy for class named...那么你在步骤5中忘记了上面的模块名称(或者说错了).
  • 如果因unrecognized selector错误而崩溃,则步骤4中的方法签名与您在步骤6中输入的方法签名不匹配.

  • “typeForIsSaved”或“typeFor”似乎不再起作用。根据[这个答案](/sf/ask/3124670111/)它应该是“typeForWithIsSaved” (2认同)

noo*_*lar 6

在Swift 4中使用Xcode 9.1 Beta,我发现迁移是可行的,但是您必须谨慎指定转换方法名称,而且似乎还需要将函数标记为@objc。

例如,我的值表达式:

FUNCTION($entityPolicy, "changeDataForData:" , $source.name)
Run Code Online (Sandbox Code Playgroud)

我的转换策略方法名称:

class StudentTransformationPolicy: NSEntityMigrationPolicy {
  @objc func changeData(forData: Data) -> String {
      return String(data: forData, encoding: .utf8)!
  }
}
Run Code Online (Sandbox Code Playgroud)

绝对棘手,在模型更改后启动我的应用程序之前,我花了很多实验才能触发它。如果所有这些都不起作用,为您的策略实施“ createDestinationInstances”可能会更容易,但是我们将把它再留一天。