Swift selector syntax

I find many examples using action: #selector, but when trying them out, Xcode (10.1) forces me to use action: Selector(("selector_name:"))

So what's the correct syntax? Probably the one Xcode accepts :) But OTOH this leads to "unrecognized selector sent to instance" errors.

Here is a quick example. In general prefer #selector because it's statically safe to use instead of the old string literal version that was only evaluated at runtime.

class Object : NSObject {
  func foo(_ selector: Selector) {
    performSelector(onMainThread: selector, with: self, waitUntilDone: false)
  }

  @objc func bar() {
    print("works")
  }
}

let object = Object()
let selector_1 = #selector(object.bar)

// Warning: Use '#selector' instead of explicitly constructing a 'Selector'
let selector_2 = Selector("bar")
// Warning: Use of string literal for Objective-C selectors is deprecated; use '#selector' instead
let selector_3: Selector = "bar"

object.foo(selector_1)
object.foo(selector_2)
object.foo(selector_3)

If you're curious enough you can read these two proposals that introduced the new behavior:

Thanks. My modified example still crashes with "unrecognized selector sent".

I assume, when the func has arguments it has to reflect in the selector (overloading)?


        let tap_selector = #selector(tappedAwayFunction(sender:)) // no syntax highlighting occurs on this line in Xcode
        let myGesture = UITapGestureRecognizer(target: self.myTextView, action: tap_selector)
        self.view.addGestureRecognizer(myGesture)
  }

@objc func tappedAwayFunction(sender: UITapGestureRecognizer){
            self.resignFirstResponder()
        }

When creating the UITapGestureRecognizer you set the target to self.myTextView which doesn't have the method tappedAwayFunction(sender:). I think you meant to set the target to self instead.

2 Likes

If it compiles then ignore the missing syntax highlighting (could be a different bug). I can't tell what's wrong with that setup on the first glance. Try adding explicit self. to your selector:

let tap_selector = #selector(self.tappedAwayFunction(sender:))

But theoretically this shouldn't be necessary. The exception that is triggered should tell you where the selector was called. When it crashes check if your object with the tappedAwayFunction is still alive or already deallocated (if your setup is not too complex then Visual Memory Graph Debugger could potentially give you more information).


Or what @cukr said. :point_up_2:

Ah, thanks. You're right. myTextView shouldn't be the receiver, it's the ViewController.