39

Is there a Simple way in Swift to iterate over the attributes of a class.

i.e. i have a class Person, and it has 3 attributes: name, lastname, age.

is there something like

for attribute in Person {
     println("\(attribute): \(attribute.value)")
}

the output would be for example:

name: Bob
lastname: Max
age: 20
iFadi
  • 915
  • 2
  • 12
  • 20
  • 2
    possible duplicate of [Does Swift support reflection?](http://stackoverflow.com/questions/24060667/does-swift-support-reflection) – Martin R Aug 23 '14 at 15:02

5 Answers5

71

They have removed reflect within Swift 2.0. This is how I am enumerating attributes and values.

class People {
    var name = ""
    var last_name = ""
    var age = 0
}

var user = People()
user.name  = "user name"
user.last_name = "user lastname"
user.age = 20

let mirrored_object = Mirror(reflecting: user)

// Swift 2
for (index, attr) in mirrored_object.children.enumerate() {
    if let property_name = attr.label as String! {
        print("Attr \(index): \(property_name) = \(attr.value)")
    }
}

// Swift 3 and later
for (index, attr) in mirrored_object.children.enumerated() {
    if let property_name = attr.label as String! {
        print("Attr \(index): \(property_name) = \(attr.value)")
    }
}

Output:
Attr 0: name = user name
Attr 1: last_name = user lastname
Attr 2: age = 20

Mehdi Ijadnazar
  • 4,532
  • 4
  • 35
  • 35
modusCell
  • 13,151
  • 9
  • 53
  • 80
13

Yes, if you just need to print properties of a struct or class, and the properties are simple types like String and Int, you can use reflect and MirrorType.

func print_properties(mirror: MirrorType) {
    for i in 0..<mirror.count {
        let (name, childMirror) = mirror[i]
        let value = childMirror.value
        println("\(i): \(name) = \(value)")
    }
}

struct Person {
    let first_name: String
    let last_name: String
    let age: Int
}

let john = Person(first_name:"John", last_name:"Doe", age:27)
let mirror = reflect(john)
print_properties(mirror)

If you have nested structs, enums, you need to do a bit more work.

Teemu Kurppa
  • 4,779
  • 2
  • 32
  • 38
  • 3
    assuming that my class is implementing a protocol. How do I get the properties of the protocol/superclass – Forke Apr 08 '15 at 14:55
5

As @mohacs suggested, You can provide a function Description in your required class like follows;

func description() -> String{

        let mirrored_object = Mirror(reflecting: self)
        let str:NSMutableString = NSMutableString()
        for (index, attr) in mirrored_object.children.enumerated() {
            if let property_name = attr.label as String! {
                str.append(" Attr \(index): \(property_name) = \(attr.value)")
            }
        }
        //print("desc=\(str)")
        return str as String
    }

Then simple call this instance method

  let jsonOb:JsonModel = self.ObjArray[Index]
  print("jsonOb = \(jsonOb.description())")
Ammar Mujeeb
  • 1,222
  • 18
  • 21
3

Apple didn't remove reflect,they just change it to _reflect,they also changed MirrorType to _MirrorType.
The Swift 2.0 version of @Teemu Kurppa 's code:

func print_properties(mirror: _MirrorType) {
  for i in 0..<mirror.count {
    let (name, childMirror) = mirror[i]
    let value = childMirror.value
    print("\(i): \(name) = \(value) ")
  }
}

struct Person {
    let first_name: String
    let last_name: String
    let age: Int
}

let john = Person(first_name:"John", last_name:"Doe", age:27)
let mirror = _reflect(john)
print_properties(mirror)
wj2061
  • 6,778
  • 3
  • 36
  • 62
2

Here's Swift 5 version of object description:

class TestClass: CustomStringConvertible {

    public var description: String {
        return Mirror(reflecting: self).children.compactMap({
            if let label = $0.label {
                return "\(label): \($0.value)"
            }
            
            return ""
        }).joined(separator: "\n")
    }
}
Maxim Makhun
  • 2,197
  • 1
  • 22
  • 26