Home > other >  Realm Swift Migration: String Wrapper List to String List
Realm Swift Migration: String Wrapper List to String List

Time:10-16

I am trying to migrate our Realm Swift schema from using a String wrapper class to the primitive String for collections. I am running into issues extracting the string values from the wrapper during migration.

Here is the wrapper class:

class StringDB: Object {
    @objc var stringVal: String = ""

    convenience init(string: String) {
        self.init()
        stringVal = string
    }
}

Then I have an example class with a List<StringDB> property:

class MyObjDB: Object {
    var emails: List<StringDB> = List<StringDB>()
    @objc dynamic var id: String = UUID().uuidString

    convenience init(_ emails: [StringDB]) {
        self.init()
        for email in emails {
            self.emails.append(objectsIn: emails)
        }
    }

    override static func primaryKey() -> String? {
        return "id"
    }
}

that I want to convert to a List<String>. Here is my migration code.

let config = Realm.Configuration(schemaVersion: latestSchemaVersion, migrationBlock: {
    migration, version in
    if version < 2 {
        migration.enumerateObjects(ofType: MyObjDB.className(), { old, new in
            guard let old = old, let new = new else { return }
            let oldEmails = old["emails"] as! List<StringDB>
            let newEmails = List<String>()
            
            for email in oldEmails {
                newEmails.append(email.stringVal)
            }
            new["emails"] = newEmails
        })
    }
})

However, the let oldEmails = old["emails"] as! List<StringDB> cast fails. I've tried also casting the collection to List<MigrationObject> and then casting the individual objects to StringDB but that cast fails as well.

I've found a workaround that may be satisfactory (I haven't confirmed yet), of converting the MigrationObject directly to string using coercion like "\(email)" and then running a regEx that will extract a desired substring from the garbage (StringDB {\n\tstringVal = [email protected];\n}), but I have no idea yet whether that will hold up in production, and I would prefer to work with something resembling a recommended way for doing this migration.

CodePudding user response:

The Realm version is not shown in the question and there are a few typo's in the code. For older Realm versions, Lists should be defined thusly:

let emails = RealmSwift.List<StringDB>() //need RealmSwift. to differentiate it from Swift List

then newer versions should be this:

@Persisted var emails = RealmSwift.List<StringDB>()

Then this is a problem as it iterates over the emails array count times and appends the entire emails list over and over for every email in that list.

for email in emails {
   self.emails.append(objectsIn: emails)
}

Also, when using older Realm versons with @Objc property types, they need to include dynamic. So on the StringDB object this

@objc var stringVal: String = ""

should be this

@objc dynamic var stringVal: String = ""

Lastly, the MyObjDB needs to have somewhere to put the new email list. You can't overwrite the old one as it's not the correct type. So add a property

class MyObjDB: Object {
    var emails: List<StringDB> = List<StringDB>()
    let updatedEmailList = List<String>()

Then to the question: See comments in code for the flow

How about this:

migration.enumerateObjects(ofType: MyObjDB.className()) { oldItem, newItem in
    //instantiate a List of the old email objects as migration objects
    let oldEmailList = oldItem!["emails"] as! List<MigrationObject>
    
    //we're going to populate a new List with the email strings
    var newEmailList = List<String>()
    
    //iterate over the old list, extracting the string from the old object as
    //  a string an inject it into a new List of Strings
    for oldEmailObject in oldEmailList {
        let oldEmail = oldEmailObject["stringVal"] as! String
        newEmailList.append(oldEmail)
    }

    //assign the new List of strings to a new emails property
    newItem!["updatedEmailList"] = newEmailList
 }
  • Related