user3352185 user3352185 - 4 months ago 25
Swift Question

Swift: reflection of protocol variables

I have a protocol with some { get } and { get set } variables and extension, where I set the former ones, and when initializing the class - to set the latter ones too. And I'd like to return all of them in a dictionary, i.e.:

protocol SomeProtocol {
var id: Int { get }
var name: String { get set }
var isItTrue: Bool { get }
}

extension SomeProtocol where Self: SomeClass {
var id: Int { return 1 }
var isItTrue: Bool { return true }

func classProps() {
var dict = [String: AnyObject]()
let mirrorSelf = Mirror(reflecting: self)
for mirrorChild in mirrorSelf.children { print(mirrorChild.label) }
}
}

class SomeClass {
// just a class, nothing special
}

class MirrorMe: SomeClass, SomeProtocol {
var name: String = "Some name"
}

class MirrorMirrorOnTheWall {
func talkToTheMirror() {
let mirrorThis = MirrorMe()
mirrorThis.classProps() // returns only "name" property
}
}


as I wrote in the comment,
classProps
returns only the protocol variable I set in the subclass. How can I include both id and isItTrue in the Mirror's children as well?

p.s. sorry for the crappy example, this is just a quick example :)

Answer

Computed properties are not represented in the runtime introspection provided by Mirror

From the Language Reference - Mirror struct (emphasis added by me)

Representation of the sub-structure and optional "display style" of any arbitrary subject instance.

Describes the parts---such as stored properties, collection elements, tuple elements, or the active enumeration case---that make up a particular instance. May also supply a "display style" property that suggests how this structure might be rendered.

The properties id (int) and isItTrue (bool) are available to instances of MirrorMe, but only as computed properties, as MirrorMe does not implement these as stored properties, but rather, make use of the default implementation of them as computed properties from extension SomeProtocol where Self: SomeClass { ... }. Hence, the computed properties id and isItTrue of MirrorMe are not contained in the sub-structure representation of MirrorMe instances, as provided by runtime introspection using Mirror.

We can verify this clearly in a more minimal example:

class Foo {
    // a stored property
    let representedInIntrospection = 0

    // a computed property
    var notRepresented: Int { return representedInIntrospection }
}

Mirror(reflecting: Foo())
    .children
    .forEach { print($0.label, $0.value) }
        /* Optional("representedInIntrospection") 0 */

To summarize: properties blueprinted in a protocol, with or without an associated default implementation, can never be stored properties on their own (default implementations of blueprinted properties may naturally only contain computed properties). This means that the only properties that are explicitly declared as stored properties in the class/struct that conforms to your protocol will show up when applying runtime introspection of an instance of such a class/struct.

Finally, you never mention the reason as for why you want a dictionary of the stored properties of a class instance, but take case if making use of this a production purpose. Generally, runtime introspection in a type safe language as Swift should only be used for diagnostics and debugging, even if it allows to be used in runtime hacks (e.g. with KVO for classes inheriting from NSObject).