'Listing all class attributes swift 3

I'm trying to print all the values from an object that inherits from a class, here is my example:

I create the class:

 class Pokemon {

 var name: String?
 var type: String?
 var level: Int?
 var exp = 0.0
}

Create the object and assign some values:

var pikachu = Pokemon()

pikachu.name = "Pika Pika"
pikachu.level = 1
pikachu.type = "electricity"
pikachu.exp = 0

Now I would like to loop through all the pikachu object attributes and print the values. I'm thinking in a for each loop but I'm not sure how to implement it.

I know I can do something like this:

func printStats(pokemon: Pokemon) {

if pokemon.name != nil {

    print(" name: \(pokemon.name!)\n level:\(pokemon.level!)\n type:\(pokemon.type!)\n exp: \(pokemon.exp!)")

 }
}

printStats(pokemon: pikachu)

output:

name: Pika Pika
level:1
type:electricity
exp: 0.0

But I just want to loop through all values, instead of explicit writing every attribute in the function.



Solution 1:[1]

I found it the way of doing it:

let pokeMirror = Mirror(reflecting: pikachu)
let properties = pokeMirror.children

for property in properties {

  print("\(property.label!) = \(property.value)")

}

output:

name = Optional("Pika Pika")
type = Optional("electricity")
level = Optional(1)
exp = Optional(0.0)

and if you want to remove the "Optional" just initialize the attributes.

Solution 2:[2]

Looks like a duplicate of Does Swift support reflection?

Alternatively, you can use a dictionary to store the attributes of Any? type.

e.g.

class Pokemon {
    var attributes = [String:Any?]()
}

var pikachu = Pokemon()

pikachu.attributes["name"] = "Pika Pika"
pikachu.attributes["level"] = 1
pikachu.attributes["type"] = "electricity"
pikachu.attributes["exp"] = 0

func printStats(pokemon: Pokemon) {
    pokemon.attributes.forEach { key, value in
        if let value = value {
            print("\(key): \(value)")
        }
    }
}

Solution 3:[3]

    1. use Mirror API to get instance's properties
    1. if you are developing iOS app, using NSObject, you may want to override description. Then can use print to print the instance.

A mirror describes the parts that make up a particular instance, such as the instance’s stored properties, collection or tuple elements, or its active enumeration case.

class YourClass: NSObject {
  public override var description: String {
        var des: String = "\(type(of: self)) :"
        for child in Mirror(reflecting: self).children {
            if let propName = child.label {
                des += "\(propName): \(child.value) \n"
            }
        }
     
        return des
    }
}

let instance = YourClass()
print(instance)

see more in Reflection in Swift

Solution 4:[4]

In Swift 5 you can create a new func in your class:

func debugLog() {
    print(Mirror(reflecting: self).children.compactMap { "\($0.label ?? "Unknown Label"): \($0.value)" }.joined(separator: "\n"))
}

And then call it with MyObject().debugLog()

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 extrablade
Solution 2 Community
Solution 3 RY_ Zheng
Solution 4 Radu Ursache