领域 Swift 错误:无法直接创建嵌入式对象

Posted

技术标签:

【中文标题】领域 Swift 错误:无法直接创建嵌入式对象【英文标题】:Realm Swift Error: Embedded objects cannot be created directly 【发布时间】: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 的回答,以快速获取列表中的项目并将其转换为字符串。 【参考方案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]

【讨论】:

以上是关于领域 Swift 错误:无法直接创建嵌入式对象的主要内容,如果未能解决你的问题,请参考以下文章

Swift iOS 应用存档错误

Discord.js 代码无法发送嵌入消息 [关闭]

无法调试嵌入在 Objective-C 应用程序中的 Swift 模块/框架

创建了对嵌入的互操作程序集间接引用,无法嵌入互操作类型

ps置入嵌入对象为啥有白色背景色

如何修复:嵌入式 H2 数据库“NonTransientError:无法读取所在位置的页面”错误?