Search code examples
swiftcomputed-properties

Check if variable is computed or stored


In my app I translate objects from custom classes into dictionaries so that they can be saved locally in a plist as well as on a server. I use the following to turn the properties of a class into a dictionary:

func dictionary() -> [String : Any] {

    var count: UInt32 = 0;
    let myClass: AnyClass = self.classForCoder;
    let properties = class_copyPropertyList(myClass, &count);

    var dictionaryRepresentation: [String:Any] = [:]

    for i in 0..<count {
        let property = properties![Int(i)]
        let cStringKey = property_getName(property);
        let key = String(cString: cStringKey!)

        dictionaryRepresentation[key] = self.value(forKey: key) as Any
    }

    return dictionaryRepresentation
}

I have a problem, however, with computed properties. It seems that those are computed and the returned value gets put into the dictionary as well, which I would like to avoid. So here is my question:

Is it possible to check whether is a property computed programatically using only its name?

I am assuming this could be possible by trying to assign a value to it which would give me an error or some similar approach.


Solution

  • Here is what seems to be a working solution, based on suggestion by dasblinkenlight.

    Rather than using the Objective-C method outlined above, create a Mirror of the class which has a children made up of all settable properties, therefore excluding computables.

    Used like this:

    let mirror = Mirror(reflecting: MyObject)
    
    for case let (label?, value) in mirror.children {
        print (label, value)
    }
    

    Here label is the name of the variable and value is obviously the value.

    EDIT: In case anyone wants to convert objects into dictionary, I am posting the full code here as well. Do however remember that if values are custom objects as well, those will need to be converted too.

    func dictionary() -> [String:Any] {
        let mirror = Mirror(reflecting: self)
    
        var dictionaryRepresentation = [String:Any]()
    
        for case let (label, value) in mirror.children {
    
            guard let key = label else { continue }
    
            dictionaryRepresentation[key] = value
        }
    
        return dictionaryRepresentation
    }