Swift 领域迁移创建从旧类型到新类型的引用

Swift Realm migration create reference from old type to new one

最初我有以下 classes:

@objcMembers public class NormalObjectRealm: Object {

    // Shared
    dynamic public var id:                String?
    dynamic public var title:             String?
    dynamic public var subTitle:          String?
    dynamic public var imageInfo:         ImageInfoRealm?
    dynamic public var descriptionString: String?
    public var categories = List<String>()
    public var count    = RealmOptional<Int>()
    public var episodes = List<String>()


    public static let realmPrimaryKey: String = "id"

    public override class func primaryKey() -> String? {
        return NormalObjectRealm.realmPrimaryKey
    }
}

@objcMembers public class ImageInfoRealm: Object {

    dynamic public var id: String?
    dynamic public var url:    String?

    public static let realmPrimaryKey: String = "id"

    public override class func primaryKey() -> String? {
        return ImageInfoRealm.realmPrimaryKey
    }

}

但现在 NormalObjectRealm 有点像这样合并到一个新的 class 中:

@objcMembers public class MediaObjectRealm: Object {

    // Shared
    dynamic public var id:                String?
    dynamic public var title:             String?
    dynamic public var subTitle:          String?
    dynamic public var imageInfo:         ImageInfoRealm?
    dynamic public var descriptionString: String?
    public var categories = List<String>()
    dynamic public var type: String?

    // NormalObjectRealm
    public var episodeCount    = RealmOptional<Int>()
    public var episodes = List<String>()

    // OtherObjectRealm
    dynamic public var urlOne:    String?
    dynamic public var urlTwo: String?
    dynamic public var urlThree:   String?
    public var isExplicit = RealmOptional<Bool>()

    public static let realmPrimaryKey: String = "id"

    public override class func primaryKey() -> String? {
        return MediaObjectRealm.realmPrimaryKey
    }
}

我目前正在尝试为此处的过渡编写迁移,其中的想法基本上是将大部分字段从 NormalObjectRealm 转移到 MediaObjectRealm

这是我的迁移块当前的样子

Realm.Configuration(schemaVersion: schemaVersion, migrationBlock: { migration, oldSchemaVersion in
            if oldSchemaVersion < temp {
                print("RealmMigration: Applying migration from \(oldSchemaVersion) to \(temp)")

                migration.enumerateObjects(ofType: "NormalObjectRealm") { oldObject, newObject in
                    guard let oldObject = oldObject else {
                        return
                    }
                    guard let id = oldObject["id"] as? String else {
                        return
                    }
                    guard let title = oldObject["title"] as? String else {
                        return
                    }

                    guard let subTitle = oldObject["subTitle"] as? String else {
                        return
                    }

                    guard let imgInfo = oldObject["imageInfo"] else {
                        return
                    }
                    guard let count = oldObject["count"] as? RealmOptional<Int>? else {
                        return
                    }

                    guard let descriptionString = oldObject["descriptionString"] as? String? else {
                        return
                    }

                    let item = migration.create("MediaObjectRealm")
                    item["id"] = id
                    item["title"] = title
                    item["subTitle"] = subTitle
                    item["descriptionString"] = descriptionString
                    item["type"] = "myType"
                    item["episodeCount"] = episodeCount // Doesn't work either...

                    migration.enumerateObjects(ofType: "ImageInfoRealm") { oldImg, newImg in
                        guard let oldImg = oldImg else {
                            return
                        }

                        let inf = oldObject.value(forKey: "imageInfo")
                        print(inf)
                        let t = migration.create("ImageInfoRealm", value: inf)
                        print("doing it")
                        //                      print(t)
                        item.setValue(t, forKey: "imageInfo")

                    }
                }
            }
    })

idtitlesubTitle 等(String?Date? 变量)设置良好并出现在新创建的 MediaObjectRealm 数据库条目。但是 ImageInfoRealm 类型的 imageInfo 不会...直接像这样设置它:item.setValue(oldObject.value(forKey: "imageInfo"), forKey: "imageInfo")(或 item["imageInfo"] = oldObject.value(forKey: "imageInfo"))导致领域崩溃并告诉我这个对象来自另一个领域,我必须把它复制过来。

'Object is already managed by another Realm. Use create instead to copy it into this Realm.'

像上面的代码一样创建它会导致甚至根本没有任何 MediaObjectRealm 类型的项目,即丢失所有数据(因为 NormalObjectRealm 也不再存在)。 我错过了什么吗?我基本上想要的是从 NormalObjectRealm 中取出 link/reference 并将其复制到新的 MediaObjectRealm.

经过长时间的测试和尝试不同的可能性,我成功地迁移了数据。 这是我为实现这一目标所做的工作。

我以此为基础:

class RealmMigrationObject {
    let migration: () -> ()

    init(migration: @escaping () -> ()) {
        self.migration = migration
    }
}

并从中导出 类。类似于:

class MigrationObjectToThree: RealmMigrationObject {

    init() {
        super.init(migration: MigrationObjectToThree.migration)
    }

    private static func migration() {
        print("Migration to three | migration")

        var imageInfos:   [ImageInfo]      = []

        let config = Realm.Configuration(schemaVersion: 3, migrationBlock: { migration, oldSchemaVersion in

            print("Migration to three | migrationBlock")
            print("RealmMigration: Applying migration from \(oldSchemaVersion) to 3")

            migration.deleteData(forType: "ExploreSectionObjectRealm")

            migration.enumerateObjects(ofType: "ImageInfoRealm") { oldInfo, newObject in
                guard let oldInfo = oldInfo else {
                    return
                }

                guard let id = oldInfo["id"] as? String,
                      let url = oldInfo["url"] as? String,
                      let url500 = oldInfo["url500"] as? String,
                      let url400 = oldInfo["url400"] as? String,
                      let url300 = oldInfo["url300"] as? String,
                      let url200 = oldInfo["url200"] as? String,
                      let url100 = oldInfo["url100"] as? String,
                      let colorString = oldInfo["color"] as? String,
                      let color = UIColor(hexString: colorString) else {
                    return
                }
                imageInfos.append(ImageInfo(id: id,
                                            url: url,
                                            url500: url500,
                                            url400: url400,
                                            url300: url300,
                                            url200: url200,
                                            url100: url100,
                                            color: color))
            }

        })


        Realm.Configuration.defaultConfiguration = config
        do {
            let realm = try Realm(configuration: config)
        print("Realm is located at: \(realm.configuration.fileURL?.description ?? "")")
            print(realm.configuration.fileURL?.description ?? "") // Printing here on purpose  as it's easier to copy
        } catch {
        print("Realm Error: \(error), trying to rebuild realm from scratch")

            let deleteMigrationConfig = Realm.Configuration(schemaVersion: RealmHelper.schemaVersion,
                                                            deleteRealmIfMigrationNeeded: true)
            do {
                _ = try Realm(configuration: deleteMigrationConfig)
            } catch {
                print("Failed to instantiate: \(error.localizedDescription)")
            }
        }

        RealmHelper.removeRealmFiles()
        Realm.Configuration.defaultConfiguration = Realm.Configuration(schemaVersion: 3)

        imageInfos.forEach({ [=11=].save() })
    }
}

据此,我刚刚针对当前模式版本和目标模式版本之间的差异创建了所有迁移,循环遍历所有迁移,只需执行该给定对象的 migration 函数。