How to loop over struct properties in Swift? How to loop over struct properties in Swift? ios ios

How to loop over struct properties in Swift?


Although old question, with Swift evolving this question has new answer. I think that you approach is way better for the described situation, however original question was how to iterate over struct properties, so here is my answer(works both for classes and structs)

You can use Mirror Structure Reference. The point is that after calling reflect to some object you get it's "mirror" which is pretty sparingly however still useful reflection.

So we could easily declare following protocol, where key is the name of the property and value is the actual value:

protocol PropertyLoopable{    func allProperties() throws -> [String: Any]}

Of course we should make use of new protocol extensions to provide default implementation for this protocol:

extension PropertyLoopable{    func allProperties() throws -> [String: Any] {        var result: [String: Any] = [:]        let mirror = Mirror(reflecting: self)        guard let style = mirror.displayStyle where style == .Struct || style == .Class else {            //throw some error            throw NSError(domain: "hris.to", code: 777, userInfo: nil)        }        for (labelMaybe, valueMaybe) in mirror.children {            guard let label = labelMaybe else {                continue            }            result[label] = valueMaybe        }        return result    }}

So now we can loop over the properties of any class or struct with this method. We just have to mark the class as PropertyLoopable.

In order to keep things static(as in the example) I will add also a singleton:

struct ReuseID: PropertyLoopable {    static let instance: ReuseID = ReuseID()}

Whether singleton used or not, we could finally loop over the properties like follows:

do {    print(try ReuseID.instance.allProperties())} catch _ {}

And that's it with looping struct properties. Enjoy swift ;)


Using hris.to's awesome answer, I wanted to provide a Swift 3 answer that's more to the point and doesn't use singletons.

Protocol & Extension:

protocol Loopable {    func allProperties() throws -> [String: Any]}extension Loopable {    func allProperties() throws -> [String: Any] {        var result: [String: Any] = [:]        let mirror = Mirror(reflecting: self)        // Optional check to make sure we're iterating over a struct or class        guard let style = mirror.displayStyle, style == .struct || style == .class else {            throw NSError()        }        for (property, value) in mirror.children {            guard let property = property else {                continue            }            result[property] = value        }        return result    }}

Example:

struct Person: Loopable {    var name: String    var age: Int}var bob = Person(name: "bob", age: 20)print(try bob.allProperties())// prints: ["name": "bob", "age": 20]


Now there's a much easier way to do this:

1: Create an Encodable protocol extension:

extension Encodable {    var dictionary: [String: Any]? {        guard let data = try? JSONEncoder().encode(self) else { return nil }        return (try? JSONSerialization.jsonObject(with: data, options: .allowFragments)).flatMap { $0 as? [String: Any] }    }}

2: Make your struct/class conform to Encodable protocol

struct MyStruct: Encodable {...}class MyClass: Encodable {...}

And then you can get a Dictionary representing your struct/class instance at any time:

var a: MyStructvar b: MyClassprint(a.dictionary)print(b.dictionary)

And then you can loop through the keys:

for (key, value) in a.dictionary { ... }for (key, value) in b.dictionary { ... }