I'm trying to get a better understanding of protocols in Swift. Specifically optional protocol methods. I thought the issue might have to do with my protocol being defined / used in a different file, but if you put the following in a playground you'll get the same issue:
import Foundation
@objc protocol MyProtocol {
optional func shouldJump() -> Bool
}
extension NSObject : MyProtocol {}
class Test {
func testJump() {
let object = NSObject()
let jump = object.shouldJump?() ?? true
print("should jump: \(jump)")
}
}
let t = Test()
t.testJump()
Here is the error message:
error: value of type 'NSObject' has no member 'shouldJump'
let jump = object.shouldJump?() ?? true
^~~~~~ ~~~~~~~~~~
For some reason it doesn't accept that the protocol has been defined on NSObject. Code completion finds it, but the compiler doesn't let it pass.
I'm not sure if my ?? true
part will work, but I want that to be a default value incase the method isn't defined.
How do I get this to work?
Your NSObject
conforms to MyProtocol
, but because it doesn't implement the optional protocol method, the compiler knows it does not have the Selector
shouldJump
:
let object = NSObject()
object.conformsToProtocol(MyProtocol) // true
object.respondsToSelector("shouldJump") // false
One way to solve this is to implement the protocol method in the extension in order for the object to perform that selector:
extension NSObject : MyProtocol {
func shouldJump() -> Bool {
// some logic here
return true
}
}
class Test {
func testJump() {
let object = NSObject()
let jump = object.shouldJump()
print("should jump: \(jump)")
}
}
let t = Test()
t.testJump() // works
If you don't want to implement the optional method in the extension, you have to cast your NSObject
as MyProtocol
and verify that it responds to the optional Selector
:
class Test {
func testJump() {
let object = NSObject()
let obj = object as MyProtocol
if object.respondsToSelector("shouldJump") {
let jump = obj.shouldJump?()
print("should jump: \(jump)")
} else {
print("nope")
}
}
}
You can also skip the respondsToSelector
step and use an if let
or guard
to verify that shouldJump()
returns non-nil.
class Test {
func testJump() {
let object = NSObject()
guard let obj: MyProtocol = object else {
return // object does not conform to MyProtocol
}
if let jump = obj.shouldJump?() { // if shouldJump() returns non-nil
print("should jump: \(jump)")
} else {
print("nope")
}
}
}
I think this is because the compiler knowns NSObject doesn't have shouldJump
method, so the call object.shouldJump?()
makes no sense. You can cast object
to your protocol:
let jump = (object as MyProtocol).shouldJump?() ?? true
Swift is a type safe language. In order to be able to use shouldJump?()
you first must have an object conformant to MyProtocol
. In this case you can simply cast your type:
let jump = (object as MyProtocol).shouldJump?() ?? true
You can also store it in a variable:
let jumper = object as MyProtocol
let jump = jumper?.shouldJump() ?? true
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With