I have 3 structs:

struct Address: Codable {
    var addressLine1: String
}
struct Person: Codable {
    var name: String
}
struct Order: Codable {
    var person: Person?
    var address: Address?
}

In my ViewController class I am using Mirror to access each of the properties within Order:

let address = Address(addressLine1: "Some unknown address")
let person = Person(name: "Some unknown name")
let order = Order(person: person, address: address)

let newOrderMirror = Mirror(reflecting: order)
newOrderMirror.children.forEach {
    display(child: $0.value)
}

In ViewController I have implemented 3 display functions:

func display(child: Any) {
    // this should never get called
    print(child)
}

func display(child: Person) {
    print(child)
}

func display(child: Address) {
    print(child)
}

In above case it is always invoking func display(child: Any), however I want it to invoke the functions with specific parameter. Is there any way to achieve this without type casting?:

Off-course this will work:

    newOrderMirror.children.forEach {
        if let bs = $0.value as? Person {
            display(child: bs)
        } else if let addr = $0.value as? Address {
            display(child: addr)
        }
        display(child: $0.value)
    }

Is there any other elegant way to achieve the desired behavior without using if-let + typecasting?

Update 1:

Found something related over here - How to call the more specific method of overloading

Update 2:

I can achieve a more concise solution by following these steps:

  1. Declare Displayable protocol with func display()
  2. Let each Modelimplement this protocol
  3. Within forEach on each children I can just bind and typecast to the protocol and invoke display method on it

However this looks like an ugly solution to me as Model is now serving 2 responsibilities - i. Handle/ Store data, ii. Handle how the data is displayed. The 2nd responsibility looks to me like more reasonable for a controller or presenter class to implement rather than the model class, hence I want to avoid that path

1

There are 1 best solutions below

0
On

The children property points to a collection of Mirror.Child elements, which is actually a tuple made of a String and an Any. Thus every child of a mirror has the compile type set to Any (this is needed as the mirror can virtually be applied to any type).

Due to the above, and given the fact that overloading is a compile-time feature, results that the compiler can't pick other overload than the one with the Any parameter.

One workaround you could make to this is to change the body of the first display() overload:

func display(child: Any) {
    switch child {
    case let person as Person: display(person)
    case let address as Address: display(address)
    default: print(child)
    }
}

func display(child: Person) {
    print(child)
}

func display(child: Address) {
    print(child)
}

Basically you still need to help the compiler pick the right function to call.