Reputation: 16539
Protocol extension and addTarget is crashing with message: Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Test.UIButton touchDown:]: unrecognized selector sent to instance 0x157eee8e0'
Where is the problem that touchDown function is unrecognized?
protocol MyButtonProtocol {
var holdTimer: NSTimer? { get set }
}
extension MyButtonProtocol where Self: UIButton {
func enable() {
addTarget(self, action: "touchDown:", forControlEvents: UIControlEvents.TouchDown)
}
mutating func touchDown(sender: UIButton) {
print("Touch down!")
holdTimer = NSTimer(timeInterval: 1, target: self, selector: Selector("didTimeOut"), userInfo: nil, repeats: true)
}
}
// Usage:
let button = UIButton()
button.enable()
Upvotes: 1
Views: 1022
Reputation: 16539
Answer from the Apple Bug Report Team:
Engineering has determined that this issue behaves as intended based on the following information: Swift protocol extensions are not available to Objective-C. Only extensions to classes can be used by Objective-C.
Upvotes: 0
Reputation: 6806
This is all very odd.
Your code
let button = UIButton()
button.enable()
seems incomplete, because the instance button
is not adopting the protocol MyButtonProtocol
.
If I write
class MyButton: UIButton, MyButtonProtocol {
var holdTimer: NSTimer?
func touch3(sender: AnyObject?) {
print("touch3 \(sender)")
}
}
then
let myButton = MyButton()
myButton.enable() // this works ok
print("touch3: \(myButton.respondsToSelector(Selector("touch3:")))")
print("touchDown: \(myButton.respondsToSelector(Selector("touchDown:")))")
print("enable \(myButton.respondsToSelector(Selector("enable")))")
then I see the output
touch3: true
touchDown: false
enable false
so the program is successfully calling the enable()
method but respondsToSelector
does not seem to be checking the methods in the protocol extension. enable
is working because if I change the call to
addTarget(self, action: "touch3:", forControlEvents: UIControlEvents.TouchDown)
then that does successfully reach touch3
.
Is that a bug in the implementation of NSObject.respondsToSelector
?
I did notice a couple of weeks ago that I could not override a function in super
with a function in a protocol extension. I thought it was just a language feature I misunderstood. Maybe that was another symptom of the same problem?
Upvotes: 1
Reputation: 1055
protocol MyButtonProtocol {
var holdTimer: NSTimer? { get set }
}
extension MyButtonProtocol where Self: UIButton {
var holdTimer: NSTimer? {
get {
return holdTimer
}
set {
holdTimer = newValue;
}
}
func enable() {
addTarget(self, action: "touchDown:", forControlEvents: UIControlEvents.TouchDown)
}
mutating func touchDown(sender: UIButton) {
print("Touch down!")
holdTimer = NSTimer(timeInterval: 1, target: self, selector: Selector("didTimeOut"), userInfo: nil, repeats: true)
}
}
extension UIButton: MyButtonProtocol {
}
// Usage:
let button = UIButton()
button.enable()
Notice the extension written for UIButton, this is needed since you have written a protocol but UIButton needs to implement it inorder for it to be effective. I have also written the getter and setter for holdTimer
in the protocol extension. You can also write this inside the UIButton
extension.
Upvotes: 0