I am trying to migrate an object with a property of type List<String>
to type List<ChildObject>
where ChildObject
is a custom EmbeddedObject
.
Example
Here's what I mean:
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 = ""
}
I'm using this code to perform the migration, which is producing the error:
Embedded objects cannot be created directly
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)
Question
How do I perform the migration while retaining the previous values?
CodePudding user response:
The easiest thing to do is to create a Dictionary
with all of the property name/value pairs of each child object and create the new List
in its entirety with an Array of those pairs.
But first you need to extract the String
values from the old List
of children
. The reason this is necessary is because Realm does not represent the element of List<String>
as an actual String
(which is a struct). We can extract the actual value from the description
field:
childrenStrings
.map { String(describing: $0) }
Once you have the name you can represent the new ChildObject
with a Dictionary
. Note that you will have to include all property names and values in the Dictionary
. Since we only have one, called "name", we include that:
childrenStrings
.map { String(describing: $0) }
.map { ["name": $0] }
Your error message said:
Embedded objects cannot be created directly
However, you can create your new objects using an Array
of Dictionary
where the Array
corresponds to the List
and each Dictionary
corresponds to a ChildObject
object. Putting it all together we have:
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"
)
}