16

I'd like to loop trough every key of mystruct and print its key and its value for every property.

struct mystruct {
  var a = "11215"
  var b = "21212"
  var c = "39932"
}

func loopthrough {
    for (key, value) in mystruct {
        print("key: \(key), value: \(value)") // Type mystruct.Type does not conform to protocol 'Sequence'
    }
}

But using the few lines from above I always get this error message:

Type mystruct.Type does not conform to protocol 'Sequence'

How can I avoid getting this message?

jscs
  • 63,694
  • 13
  • 151
  • 195
  • You can not do this in swift, but may be in future as it is a proposed solution you can check it https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md but you can get your solution here https://stackoverflow.com/questions/27292255/how-to-loop-over-struct-properties-in-swift – Vikky Jan 27 '18 at 13:20
  • see this https://airspeedvelocity.net/2014/07/13/swift-structs-and-accessing-properties-by-name/ – Prashant Tukadiya Jan 27 '18 at 13:22
  • Possible duplicate of https://stackoverflow.com/questions/24844681/list-of-classs-properties-in-swift. – Martin R Jan 27 '18 at 13:23

4 Answers4

59

First of all let's use CamelCase for the struct name

struct MyStruct {
    var a = "11215"
    var b = "21212"
    var c = "39932"
}

Next we need to create a value of type MyStruct

let elm = MyStruct()

Now we can build a Mirror value based on the elm value.

let mirror = Mirror(reflecting: elm)

The Mirror value does allow us to access all the properties of elm, here's how

for child in mirror.children  {
    print("key: \(child.label), value: \(child.value)")
}

Result:

key: Optional("a"), value: 11215

key: Optional("b"), value: 21212

key: Optional("c"), value: 39932

Luca Angeletti
  • 58,465
  • 13
  • 121
  • 148
4

use following code to get array of all the properties

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

extension PropertyLoopable {
    func allProperties() throws -> [String] {

        var result: [String] = []

        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,_) in mirror.children {
            guard let property = property else {
                continue
            }
            result.append(property)
         //   result[property] = value
        }

        return result
    }
}

Now just

let allKeys = try  self.allProperties()

Don't forgot to implement protocol

Hope it is helpful

Prashant Tukadiya
  • 15,838
  • 4
  • 62
  • 98
4

You can use runtime introspection (on an instance of your type) combined with value-binding pattern matching to extract the property names and values; the latter used to unwrap the optional label property of the Mirror instance used to represent the sub-structure of your specific instance.

E.g.:

struct MyStruct {
    let a = "11215"
    let b = "21212"
    let c = "39932"
}

// Runtime introspection on an _instance_ of MyStruct
let m = MyStruct()
for case let (label?, value) in Mirror(reflecting: m)
    .children.map({ ($0.label, $0.value) }) {
    print("label: \(label), value: \(value)")
} /* label: a, value: 11215
     label: b, value: 21212
     label: c, value: 39932 */
dfrib
  • 70,367
  • 12
  • 127
  • 192
1

I hope it still helps someone: This is my version of the protocol for more complicated classes/structs (Objects within Objects within Objects ;-) ) I am sure there is a more elegant functional solution but this was a quick and dirty solution, as I only needed it for a temporary log.

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


extension PropertyLoopable {
func allProperties() -> [String: Any] {
    
    var result: [String: Any] = [:]
    let mirror = Mirror(reflecting: self)
    
    // make sure we're iterating over a struct or class
    guard let style = mirror.displayStyle, style == .struct || style == .class else {
        print("ERROR: NOT A CLASS OR STRUCT")
        return result
    }
    
    for (property, value) in mirror.children {
        guard let property = property else {
            continue
        }
        // It was a very complicated struct from a JSON with a 4 level deep structure. This is dirty dancing, remove unnecessary "for" loops for simpler structs/classes
        // if value from property is not directly a String, we need to keep iterating one level deeper
        if value is String {
            result.updateValue(value, forKey: property)
        } else {
            let mirror = Mirror(reflecting: value)
            
            for (property, value) in mirror.children {
                guard let property = property else {
                    continue
                }
                //let's go for a second level
                if value is String {
                    result.updateValue(value, forKey: property)
                } else {
                    let mirror = Mirror(reflecting: value)
                    
                    for (property, value) in mirror.children {
                        guard let property = property else {
                            continue
                        }
                        //3rd level
                        if value is String {
                            result.updateValue(value, forKey: property)
                        } else {
                            let mirror = Mirror(reflecting: value)
                            
                            for (property, value) in mirror.children {
                                guard let property = property else {
                                    continue
                                }
                                result.updateValue(value, forKey: property)
                            }
                        }
                    }
                }
            }
        }
    }
    return result
}

}

Santi Pérez
  • 370
  • 4
  • 12
  • A cleaner solution that uses recursion here ;) https://stackoverflow.com/questions/27292255/how-to-loop-over-struct-properties-in-swift/56675017#56675017 – RPatel99 Jun 19 '19 at 20:00