Search code examples
swiftrealm

Local migration with List<String> in swift


With Realm 5.4.0 I'm trying to migrate:

public final class MyObject: Object {
   public let tags = List<String>()
}

to

public final class MyObject: Object {
   public let tagIds = List<String>()
}

Therefore I wanted to write a migration block like this:

migration.enumerateObjects(ofType: "MyObject") { oldObject, newObject in
   let tags = [String: String]() // filled earlier
   let oldTagList = oldObject!.dynamicList("tags")
   let newTagList = newObject!.dynamicList("tagIds")

   oldTagList.forEach { tag in
      if let id = tags[tag as String] { // this does not compile (1)
         newTagList.append(id) // this does not work (2)
      }
   }
}

From the oldTagList I get DynamicObjects which cannot be casted to strings (1). I was able to solve this problem by a very ugly workaround:

let oldTagList = unsafeBitCast(oldObject!["tags"] as! List<DynamicObject>, to: List<String>.self)

What I was not able to solve is to add the id string to newObjects tagIds list (2).

I tried to cast the list the same way than oldObjects tags list but that throws an EXC_BAD_ACCESS error.

How can I migrate / convert the content of a List<String> into another List<String>?


Solution

  • The objective in the question is to create a new tag List for the updated objects with different tags based on a dictionary lookup. Let me set this up for clarity

    Here's the existing object with a tag List (of strings)

    object_0
       tags = ["a", "b", "c"]
    

    and the new object will look like this

    object_0
       tagIds = ["tag_a", "tag_b", "tag_c"]
    

    the lookup is done through an existing dictionary that would look like this

    tags ["a": "tag_a",
          "b": "tag_b",
          "c": "tag_c"]
    

    So a maps to tag_a, b maps to tag_b etc

    Here's the migration block to do that

    migration.enumerateObjects(ofType: MyObject.className()) { oldItem, newItem in
        let oldTags = oldItem!["tags"] as! List<MigrationObject>
        let tagCount = oldTags.count
        
        let updatedList = List<String>()
        
        for index in 0..<tagCount {
            let oldTag = oldTags[index]
            let val = "\(oldTag)" //make the NSTaggedPointerString a String
            //let val = oldTag.description this may work as well
            
            if let updatedTag = self.tags[val] {
                updatedList.append(updatedTag)
            }
        }
        
        newItem!["tagIds"] = updatedList
     }