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")
}
}
}
})
id
、title
、subTitle
等(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
函数。
最初我有以下 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")
}
}
}
})
id
、title
、subTitle
等(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
函数。