Swift 3 protocol extension using selector error
ios, objective-c, protocols, swift, swift3
Solution
This is a Swift protocol extension. Swift protocol extensions are invisible to Objective-C, no matter what; it knows nothing of them. But `#selector` is about Objective-C seeing and calling your function. That is not going to happen because your `on(tap:)` function is defined only in the protocol extension. Thus the compiler rightly stops you.
This question is one of a large class of questions where people think they are going to be clever with protocol extensions in dealing with Cocoa by trying to inject Objective-C-callable functionality (selector, delegate method, whatever) into a class via a protocol extension. It's an appealing notion but it's just not going to work.
Problem
I have what I thought to be a very simple protocol extension for my `UIViewController`s providing the capability to dismiss a keyboard through a tap gesture. Here's my code: ``` @objc protocol KeyboardDismissing { func on(tap: UITapGestureRecognizer) } extension KeyboardDismissing where Self: UIViewController { func addDismissalGesture() { let tap = UITapGestureRecognizer(target: self, action: #selector(Self.on(tap:))) view.addGestureRecognizer(tap) } func on(tap: UITapGestureRecognizer) { dismissKeyboard() } func dismissKeyboard() { view.endEditing(true) } } ``` The problem is that the above code throws a compile error on this line: ``` let tap = UITapGestureRecognizer(target: self, action: #selector(Self.on(tap:))) ``` With the error message: Argument of '#selector' refers to instance method 'on(tap:)' that is not exposed to Objective-C with the suggestion to "fix it" by adding `@objc` before `func on(tap: UITapGestureRecognizer)` Ok fine, I add the tag: ``` @objc func on(tap: UITapGestureRecognizer) { dismissKeyboard() } ``` But then, it throws a different compile error on this newly added `@objc` tag with the error message: @objc can only be used with members of classes, @objc protocols, and concrete extensions of classes with the suggestion to "fix it" by removing the exact same tag I was just told to add. I originally thought adding `@objc` before my protocol definition would solve any `#selector` problems but apparently that's not the case, and these cyclical error messages/suggestions aren't helping in the slightest. I've gone down a wild goose chase of adding/removing `@objc` tags everywhere, marking methods as `optional`, putting methods in the protocol's definition, etc. It also doesn't matter what I put in the protocol definition Leaving the extension the same, the following example does not work nor does any combination of the declared methods in the protocol's definition: ``` @objc protocol KeyboardDismissing { func on(tap: UITapGestureRecognizer) } ``` This tricks me into thinking it works by compiling as a stand alone protocol, but the second I try to add it to a view controller: ``` class ViewController: UIViewController, KeyboardDismissing {} ``` it spits back the original error. Can someone explain what I'm doing wrong and how I can compile this? Note: I've looked at this question but it is for Swift 2.2 not Swift 3 nor does the answer compile as soon as you create a view controller class that inherits from the protocol defined in the example. I've also looked at this question but the answer uses `NotificationCenter` which is not what I'm after. If there are any other seemingly duplicate questions, please let me know.