nmdias
nmdias

Reputation: 3948

How to get a property name and its value using Swift 2.0, and reflection?

Given this Model:

public class RSS2Feed {

    public var channel: RSS2FeedChannel?

    public init() {}
}

public class RSS2FeedChannel {   

    public var title: String?
    public var description: String?

    public init() {}

}

What would I need to do in order to get the property names and values of an RSS2FeedChannel instance?

Here's what I'm trying:

let feed = RSS2Feed()
feed.channel = RSS2FeedChannel()
feed.channel?.title = "The Channel Title"

let mirror = Mirror(reflecting: feed.channel)
mirror.children.first // ({Some "Some"}, {{Some "The Channel Title...

for (index, value) in mirror.children.enumerate() {
    index // 0
    value.label // "Some"
    value.value // RSS2FeedChannel
}

Ultimately, I'm trying to create a Dictionary that matches the instance, using reflection, but so far I'm unable to get the properties name and values of the instance.

Documentation says that:

The optional label may be used when appropriate, e.g. to represent the name of a stored property or of an active enum case, and will be used for lookup when Strings are passed to the descendant method.

Yet I only get a "Some" string.

Also, the value property is returning a string with the Type RSS2FeedChannel when I would expect each children to be "An element of the reflected instance's structure."!

Upvotes: 5

Views: 4629

Answers (2)

LoVo
LoVo

Reputation: 2073

When i understand correct this should solve ur problem:

func aMethod() -> Void {
    let feed = RSS2Feed()
    feed.channel = RSS2FeedChannel()
    feed.channel?.title = "The Channel Title"
//  feed.channel?.description = "the description of your channel"

    guard  let channel = feed.channel else {
        return
    }

    let mirror = Mirror(reflecting: channel)
    for child in mirror.children {
        guard let key = child.label else {
            continue
        }
        let value = child.value

        guard let result = self.unwrap(value) else {
            continue
        }

        print("\(key): \(result)")
    }
}

private func unwrap(subject: Any) -> Any? {
    var value: Any?
    let mirrored = Mirror(reflecting:subject)
    if mirrored.displayStyle != .Optional {
        value = subject
    } else if let firstChild = mirrored.children.first {
        value = firstChild.value
    }
    return value
}

just some little changes for swift 3:

private func unwrap(_ subject: Any) -> Any? {
    var value: Any?
    let mirrored = Mirror(reflecting:subject)
    if mirrored.displayStyle != .optional {
        value = subject
    } else if let firstChild = mirrored.children.first {
        value = firstChild.value
    }
    return value
}

Upvotes: 5

Sandy Chapman
Sandy Chapman

Reputation: 11341

You can use the descendent method on the Mirror object to get this information. It will return nil if the values aren't found or the optionals contain no value.

let mirror = Mirror(reflecting: feed.channel)
let child1 = mirror.descendant("Some", "title") // "The Channel Title"

// or on one line
let child3 = Mirror(reflecting: feed).descendant("channel", "Some", "title")

Upvotes: 1

Related Questions