Search code examples
swiftfunctioncastinganyobject

Make a Functions Able to Handle Different Classes as Parameters


I'm trying to pass different classes, with the same properties, to the same function. How do I cast them for use with this function?

Below is a simple example to highlight what I'm trying to achieve.

class A {
    var height:Int = 10
}

class B {
    var height:Int = 20
}

class C {
    static func grow(class:AnyObject) {
        class.height + 5
    }
}


C.grow(A)
C.grow(B)

The two final calls should yield 15 and 25, but without casting the AnyObject back to A or B, an error like the following is generated: "AnyObject has no member named".

How do I accomplish something like this?


Solution

  • Swift reflection API enables you to read values but not modify them. So if that's enough for you, you may use a method similar to the following which takes an object and the label of the member you want to access:

    func getValue<T>(object: AnyObject, memberLabel: String) -> T? {
        let mirror = Mirror(reflecting: object)
        for member in mirror.children {
            if let _ = member.label where member.label == memberLabel,
               let value = member.value as? T {
                return value
            }
        }
        return nil
    }
    

    But if you want to modify the values, you have to define a protocol and make the classes conform to it:

    protocol HasHeight {
        var height: Int { get set }
    }
    
    extension HasHeight {
        mutating func grow() {
            self.height += 5
        }
    }
    
    class A : HasHeight {
        var height = 10
    }
    
    class B : HasHeight {
        var height = 20
    }
    
    var a = A()
    print(a.height)
    a.grow()
    print(a.height)
    
    var b = B()
    print(b.height)
    b.grow()
    print(b.height)
    

    Here I defined grow() as a protocol extension so that it is available on every class/struct that conforms to the HasHeight protocol.

    The results are:

    10

    15

    20

    25

    You may define it elsewhere, but the call will have to be changed to include an & reference:

    func grow<T: HasHeight>(inout sized: T) {
        sized.height += 5
    }
    
    grow(&b)