Please note that this issue has been resolved with the release of Swift 2.2 – see “Explicit Member Expression” section of The Swift Programming Language book.
In swift you can make an optional call to a method that that may or may not be implemented in conformance to a protocol:
@objc protocol F {
optional func f(#p1: String, p2: String) -> String
}
@objc class C: F {
func f(#p1: String, p2: String) -> String { return "0 \(p1) \(p2)" }
}
let c: F = C()
c.f?(p1: "1", p2: "2") // --> "0 1 2"
Or you can also optionally bind the method to a local constant:
if let f = c.f {
f(p1: "1", p2: "2")
}
This is an elegant way to wrap some code that should only happen if the method is implemented, not just the call itself. Notice, however, that unlike the respondsToSelector:
of the NSObjectProtocol
, optional binding does not check for the named parameters. So, if you have two methods that only differ in the parameter names, you get Ambiguous use of 'f'
compile time error:
@objc protocol F {
optional func f(#p1: String, p2: String) -> String
optional func f(#x: String, y: String) -> String
}
@objc class C: F {
func f(#p1: String, p2: String) -> String { return "0 \(p1) \(p2)" }
func f(#x: String, y: String) -> String { return "0 \(x) \(y)" }
}
let c: F = C()
c.f?(p1: "1", p2: "2") // --> "0 1 2"
c.f?(x: "1", y: "2") // --> "0 1 2"
if let f = c.f { // Ambiguous use of 'f'
f(p1: "1", p2: "2")
}
This then is an issue of Swift - Obj C interoperability. Or have I missed something and there is in fact some Swift syntax available that would disambiguate between the two methods? Or should I quit designing code that makes use of optional methods and simply have multiple protocols to cater for diversity of needs (which may in turn cram type definitions with a lot of protocol names)?
Edit
My initial thinking was along the lines of Sulthan
's answer below. However, I am now beginning to lean more towards his original comment: "This seems to be an omission in the grammar. I would report it." The reason for this has to do with the analogue case of referencing instance methods as curried functions of the type:
class C {
func f() -> String { return "I'm a C" }
}
let c = C()
c.f() //--> "I'm a C"
let f = C.f(c)
f() //--> "I'm a C"
This is nice and people are already making good use of this fundamental feature of Swift.
Of course you can do the same with named parameters:
class D {
func g(#x: Int) -> String { return "Gave me \(x)" }
func g(#y: Int) -> String { return "Give me \(y)" }
}
let d = D()
d.g(x: 5) //--> "Gave me 5"
d.g(y: 15) //--> "Gave me 15"
D.g(d)(x: 5) //--> "Gave me 5"
Only again, as with optional methods above, I do not know how to disambiguate between 'f' and 'g' without the immediate call:
let g = D.g(d) // Ambiguous use of 'g'
g(x: 5)
Note that type annotation would help (given the types were declared as differen, of course):
class D {
func g(#x: Int) -> String { return "Gave me \(x)" }
func g(#y: Double) -> String { return "Give me \(y)" }
}
let d = D()
let g: (x: Int) -> String = D.g(d)
g(x: 5) //--> "Gave me 5"
However, the compiler ignores the parameter name in (x: Int)
and the reference would still be ambiguous if both x
and y
were an Int
...
Similarly:
let p = (x: 0, y: 1)
p is (a: Int, b: Int) //--> true
even though:
let q: (a: Int, b: Int) = (x: 0, y: 1) //--> Error: '(x: Int, y: Int)' is not convertible to '(a: Int, b: Int)`