realm-swift: setValue forKey not working with List

I have an object in Realm. I would like to retrieve that object and then work with an unmanaged version of that object. Unfortunately, it doesn’t seem like Realm for iOS has any good copy options, so I tried to followed the “detachable” workaround by “anlaital” outlined in issue #3381.

My code is as follows: screen shot 2017-10-27 at 3 53 33 pm

This works for the most part. When on a property that is a list and has values, detachable.detached() does return the expect list copy. Unfortunately, detached.setValue(detachable.detached(), forKey: property.name) does not set the value for any Lists my object has. screen shot 2017-10-27 at 3 54 39 pm

Any thoughts on how I can properly assign my “detached”/unmanaged list to my object?

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 1
  • Comments: 17 (4 by maintainers)

Most upvoted comments

I actually have a slightly newer version of this.


protocol DetachableObject: AnyObject {
    func detached() -> Self
}

extension Object: DetachableObject {
    
    func detached() -> Self {
        let detached = type(of: self).init()
        for property in objectSchema.properties {
            guard let value = value(forKey: property.name) else { continue }
            
            if property.isArray == true {
                //Realm List property support
                let detachable = value as? DetachableObject
                detached.setValue(detachable?.detached(), forKey: property.name)
            } else if property.type == .object {
                //Realm Object property support
                let detachable = value as? DetachableObject
                detached.setValue(detachable?.detached(), forKey: property.name)
            } else {
                detached.setValue(value, forKey: property.name)
            }
        }
        return detached
    }
}

extension List: DetachableObject {
    func detached() -> List<Element> {
        let result = List<Element>()
        
        forEach {
            if let detachable = $0 as? DetachableObject {
                let detached = detachable.detached() as! Element
                result.append(detached)
            } else {
                result.append($0) //Primtives are pass by value; don't need to recreate
            }
        }
        
        return result
    }
    
    func toArray() -> [Element] {
        return Array(self.detached())
    }
}

extension Results {
    func toArray() -> [Element] {
        let result = List<Element>()
        
        forEach {
            result.append($0)
        }
        
        return Array(result.detached())
    }
}

The documentation is either unclear or wrong. I think the original intent was to say you can modify the elements inside a List, including assigning new objects to a particular index, rather than assigning a new List instance altogether. I’ll update it, thanks for bringing it to our attention.

Hi, can someone translate @Alarson93’s solution in Objective C.

@allenhumphreys Comment on my other issue (#5469) gave me a magnificent idea. Instead of having the messy casting for lists like I do in post 4 (which incidentally crashes for objects), because this is a detached/unmanaged object, I should be able to safely do setValueForKey for lists.

I tested this and it seemed to work, but I would like to hear your thoughts @austinzheng. Do you think it is safe to set/assign a list in this manner on the premise that everything is unmanaged?

screen shot 2017-11-15 at 12 51 15 pm

For what it’s worth, I’ve had success with this approach: screen shot 2017-10-27 at 11 23 22 pm

I’m appending to lists (reference grabbed via getValue:forKey: ). The unfortunate part is I must be quite verbose when checking list types (note the as? List<Int>, as? List<String, and so on).