A function
func stepperValueChanged(_ myStepper: UIStepper) {
// do stuff with myStepper
}
A second function
func switchValueChanged(_ mySwitch: UISwitch) {
// do stuff with mySwitch
}
How do I create a third (alternative) function that can take either type?
func valueChanged(_ myComponent: /* ??? UIStepper or UISwitch, but nothing else ??? */) {
// do stuff with myComponent
}
I've explored using enums, typealiases and protocols; which resulted in lots of interesting Stackoverflow reads but no solution.
Examples that don't work
// ** DON'T COPY AND PASTE, DONT WORK!! ** //
typealias validUIComponent = UIStepper, UISwitch
// or
typealias validUIComponent = UIStepper & UISwitch
// or
enum UIComponent { case stepper(UIStepper); case _switch(UISwitch) }
// or
protocol UIComponent { }
extension UIStepper: UIComponent { }
extension UISwitch: UIComponent { }
// ** DON'T COPY AND PASTE, DONT WORK!! ** //
Why would I want to do this? Type checking. I don't want any other UI element to be passed to the function.
I realise I could if let/guard let or some other form of checking once in the function body and bail as required, but this would only catch run-time not compile-time type errors.
Also I realise I could use Any? or (better) UIControl and downcast as needed.
func valueChanged(_ myComponent: UIControl) {
// do stuff with
myComponent as! UIStepper
// do stuff with
myComponent as! UISwitch
}
But is there a syntactic/more expressive solution?
You mentioned enums, which sound like an excellent fit for this use case.
You can explicitly only require the types you expect, and nothing else.
By adding a property to the enum you can then expose a UIControl
property to interact as needed, without the need for down-casting (which is generally considered to be an anti-pattern).
enum Component {
case `switch`(UISwitch)
case stepper(UIStepper)
var control: UIControl {
switch self {
case .switch(let comp):
return comp
case .stepper(let comp):
return comp
}
}
}
Then ask for a Component
as a parameter to the function.
func controlValueChanged(_ myComponent: Component) {
// Now you can use them as a generic UIControl
let control = myComponent.control
// ...or different behaviours for each element
switch myComponent {
case .switch(let swit):
// use the `swit`
case .stepper(let step):
// use the `step`
}
}
Having said that, if the implementations for these types are totally different anyway, it may be more clear to define two separate functions.