【问题标题】:Realm Swift Error: Embedded objects cannot be created directly领域 Swift 错误:无法直接创建嵌入式对象
【发布时间】:2021-11-11 01:27:37
【问题描述】:

我正在尝试将具有List<String> 类型属性的对象迁移到List<ChildObject> 类型,其中ChildObject 是自定义EmbeddedObject

示例

这就是我的意思:

import RealmSwift

final class ParentObject: Object {
    // Previously, this property was of type `List<String>`.
    @Persisted public var children: List<ChildObject>
}

final class ChildObject: EmbeddedObject {
    @Persisted var name = ""
}

我正在使用此代码执行迁移,这会产生错误:

不能直接创建嵌入式对象

let configuration = Realm.Configuration(schemaVersion: 1) { migration, oldSchemaVersion in
    if oldSchemaVersion < 1 {
        migration.enumerateObjects(ofType: ParentObject.className()) { oldObject, newObject in
            let childrenStrings = oldObject!["children"] as! List<DynamicObject>
            let childrenObjects = newObject!["children"] as! List<MigrationObject>

            // I'm trying to retain the previous values for `children` (of type `String`) 
            // where each value is used as the `name` property of a new `ChildObject`.
            for string in childrenStrings {
                childrenObjects.append(
                    // This line produces the error :(
                    migration.create(ChildObject.className(), value: [string])
                )
            }
        }
    }
}

let realm = try! Realm(configuration: configuration)

问题

如何在保留先前值的同时执行迁移?

【问题讨论】:

  • 既然你只是用旧对象的值添加一个新对象,为什么不像你一样迭代childrenStrings并在那个循环中创建新对象let c = ChildObject()分配值@ 987654329@并将其添加到对象中?
  • 嗨,杰。我遇到了几个类型不匹配的问题。在c.name = string,我得到Cannot assign value of type 'DynamicObject'(到String)。正如 Rob 所说,我可以使用 String(describing:) 解决这个问题。但是,由于newObject!["children”]ChildObject (c) 之间的类型不匹配,您说“将其添加到对象”时我有点迷失了。
  • 实际上,这似乎运作良好:newObject!["children"] = childrenStrings.map { /* create new child object and assign name */ } as [ChildObject]。感谢您指出这一点。
  • 酷。我认为这可能有效。您提到的那个错误是因为字符串转换为List&lt;DynamicObject&gt;。请参阅我对this question 的回答,以快速获取列表中的项目并将其转换为字符串。

标签: swift realm realm-migration realm-embedded-object


【解决方案1】:

最简单的做法是使用每个子对象的所有属性名称/值对创建一个Dictionary,并使用这些对的数组创建一个完整的新List

但首先您需要从 children 的旧 List 中提取 String 值。这是必要的原因是因为 Realm 不会将 List&lt;String&gt; 的元素表示为实际的 String(这是一个结构)。我们可以从description字段中提取实际值:

childrenStrings
    .map { String(describing: $0) }

一旦你有了名字,你就可以用Dictionary 来代表新的ChildObject。请注意,您必须在 Dictionary 中包含所有属性名称和值。由于我们只有一个,称为“名称”,因此我们将其包括在内:

childrenStrings
    .map { String(describing: $0) }
    .map { ["name": $0] }

你的错误信息说:

不能直接创建嵌入式对象

但是,您可以使用Dictionary 中的Array 创建新对象,其中Array 对应于List,每个Dictionary 对应一个ChildObject 对象。综上所述,我们有:

migration.enumerateObjects(ofType: ParentObject.className()) { oldObject, newObject in
    let childrenStrings = oldObject!["children"] as! List<DynamicObject>

    newObject?.setValue(
        Array(
            childrenStrings
                .map { String(describing: $0) }
                .map { ["name": $0] }
        ),
        forKey: "children"
    )
}

【讨论】:

  • 好的,谢谢!我可能会使用Array(childrenStrings.map { [$0] }) 之类的值来设置值以避免映射两次,但这很好。
【解决方案2】:

分别总结 Rob 和 Jay 的解决方案:

选项 1

这是 Rob 修改后的解决方案。

migration.enumerateObjects(ofType: ParentObject.className()) { oldObject, newObject in
    let childrenStrings = oldObject!["children"] as! List<DynamicObject>

    newObject!.setValue(
        Array(childrenStrings.map { [$0] }), 
        forKey: "children"
    )
}

选项 2

这是基于 Jay 建议的解决方案。它创建的数组(或字典,如果使用命名参数)更少,并且应该使用更复杂的对象更好地扩展。

migration.enumerateObjects(ofType: ParentObject.className()) { oldObject, newObject in
    let childrenStrings = oldObject!["children"] as! List<DynamicObject>

    newObject!["children"] = childrenStrings.map {
        let childObject = ChildObject()
        childObject.name = "\($0)"
        return childObject
    } as [ChildObject]
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-09-10
    • 2017-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-06
    • 1970-01-01
    相关资源
    最近更新 更多