[swift] @selector() in Swift?

I'm trying to create an NSTimer in Swift but I'm having some trouble.

NSTimer(timeInterval: 1, target: self, selector: test(), userInfo: nil, repeats: true)

test() is a function in the same class.


I get an error in the editor:

Could not find an overload for 'init' that accepts the supplied arguments

When I change selector: test() to selector: nil the error disappears.

I've tried:

  • selector: test()
  • selector: test
  • selector: Selector(test())

But nothing works and I can't find a solution in the references.

This question is related to swift selector nstimer

The answer is


Here's a quick example on how to use the Selector class on Swift:

override func viewDidLoad() {
    super.viewDidLoad()

    var rightButton = UIBarButtonItem(title: "Title", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("method"))
    self.navigationItem.rightBarButtonItem = rightButton
}

func method() {
    // Something cool here   
}

Note that if the method passed as a string doesn't work, it will fail at runtime, not compile time, and crash your app. Be careful


selector is a word from Objective-C world and you are able to use it from Swift to have a possibility to call Objective-C from Swift It allows you to execute some code at runtime

Before Swift 2.2 the syntax is:

Selector("foo:")

Since a function name is passed into Selector as a String parameter("foo") it is not possible to check a name in compile time. As a result you can get a runtime error:

unrecognized selector sent to instance

After Swift 2.2+ the syntax is:

#selector(foo(_:))

Xcode's autocomplete help you to call a right method


I found many of these answers to be helpful but it wasn't clear how to do this with something that wasn't a button. I was adding a gesture recognizer to a UILabel in swift and struggled so here's what I found worked for me after reading everything above:

let tapRecognizer = UITapGestureRecognizer(
            target: self,
            action: "labelTapped:")

Where the "Selector" was declared as:

func labelTapped(sender: UILabel) { }

Note that it is public and that I am not using the Selector() syntax but it is possible to do this as well.

let tapRecognizer = UITapGestureRecognizer(
            target: self,
            action: Selector("labelTapped:"))

// for swift 2.2
// version 1
buttton.addTarget(self, action: #selector(ViewController.tappedButton), forControlEvents: .TouchUpInside)
buttton.addTarget(self, action: #selector(ViewController.tappedButton2(_:)), forControlEvents: .TouchUpInside)

// version 2
buttton.addTarget(self, action: #selector(self.tappedButton), forControlEvents: .TouchUpInside)
buttton.addTarget(self, action: #selector(self.tappedButton2(_:)), forControlEvents: .TouchUpInside)

// version 3
buttton.addTarget(self, action: #selector(tappedButton), forControlEvents: .TouchUpInside)
buttton.addTarget(self, action: #selector(tappedButton2(_:)), forControlEvents: .TouchUpInside)

func tappedButton() {
  print("tapped")
}

func tappedButton2(sender: UIButton) {
  print("tapped 2")
}

// swift 3.x
button.addTarget(self, action: #selector(tappedButton(_:)), for: .touchUpInside)

func tappedButton(_ sender: UIButton) {
  // tapped
}

button.addTarget(self, action: #selector(tappedButton(_:_:)), for: .touchUpInside)

func tappedButton(_ sender: UIButton, _ event: UIEvent) {
  // tapped
}

For swift 3

let timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(self.test), userInfo: nil, repeats: true)

Function declaration In same class:

@objc func test()
{
    // my function
} 

For future readers, I found that I experienced a problem and was getting an unrecognised selector sent to instance error that was caused by marking the target func as private.

The func MUST be publicly visible to be called by an object with a reference to a selector.


Swift 4.0

you create the Selector like below.

1.add the event to a button like:

button.addTarget(self, action: #selector(clickedButton(sender:)), for: UIControlEvents.touchUpInside)

and the function will be like below:

@objc func clickedButton(sender: AnyObject) {

}

Swift 4.1
With sample of tap gesture

let gestureRecognizer = UITapGestureRecognizer()
self.view.addGestureRecognizer(gestureRecognizer)
gestureRecognizer.addTarget(self, action: #selector(self.dismiss(completion:)))

// Use destination 'Class Name' directly, if you selector (function) is not in same class.
//gestureRecognizer.addTarget(self, action: #selector(DestinationClass.dismiss(completion:)))


@objc func dismiss(completion: (() -> Void)?) {
      self.dismiss(animated: true, completion: completion)
}

See Apple's document for more details about: Selector Expression


When using performSelector()

/addtarget()/NStimer.scheduledTimerWithInterval() methods your method (matching the selector) should be marked as

@objc
For Swift 2.0:
    {  
        //...
        self.performSelector(“performMethod”, withObject: nil , afterDelay: 0.5)
        //...


    //...
    btnHome.addTarget(self, action: “buttonPressed:", forControlEvents: UIControlEvents.TouchUpInside)
    //...

    //...
     NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector : “timerMethod”, userInfo: nil, repeats: false)
    //...

}

@objc private func performMethod() {
…
}
@objc private func buttonPressed(sender:UIButton){
….
}
@objc private func timerMethod () {
….
}

For Swift 2.2, you need to write '#selector()' instead of string and selector name so the possibilities of spelling error and crash due to that will not be there anymore. Below is example

self.performSelector(#selector(MyClass.performMethod), withObject: nil , afterDelay: 0.5)

If you want to pass a parameter to the function from the NSTimer then here is your solution:

var somethingToPass = "It worked"

let timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: "tester:", userInfo: somethingToPass, repeats: false)

func tester(timer: NSTimer)
{
    let theStringToPrint = timer.userInfo as String
    println(theStringToPrint)
}

Include the colon in the selector text (tester:), and your parameter(s) go in userInfo.

Your function should take NSTimer as a parameter. Then just extract userInfo to get the parameter that passed.


Just in case somebody else have the same problem I had with NSTimer where none of the other answers fixed the issue, is really important to mention that, if you are using a class that do not inherits from NSObject either directly or deep in the hierarchy(e.g. manually created swift files), none of the other answers will work even when is specified as follows:

let timer = NSTimer(timeInterval: 1, target: self, selector: "test", 
                    userInfo: nil, repeats: false)
func test () {}

Without changing anything else other than just making the class inherit from NSObject I stopped getting the "Unrecognized selector" Error and got my logic working as expected.


Create Refresh control using Selector method.   
    var refreshCntrl : UIRefreshControl!
    refreshCntrl = UIRefreshControl()
    refreshCntrl.tintColor = UIColor.whiteColor()
    refreshCntrl.attributedTitle = NSAttributedString(string: "Please Wait...")
    refreshCntrl.addTarget(self, action:"refreshControlValueChanged", forControlEvents: UIControlEvents.ValueChanged)
    atableView.addSubview(refreshCntrl)

//Refresh Control Method

func refreshControlValueChanged(){
    atableView.reloadData()
    refreshCntrl.endRefreshing()

}

Change as a simple string naming in the method calling for selector syntax

var timer1 : NSTimer? = nil
timer1= NSTimer(timeInterval: 0.1, target: self, selector: Selector("test"), userInfo: nil, repeats: true)

After that, type func test().


For Swift 3

//Sample code to create timer

Timer.scheduledTimer(timeInterval: 1, target: self, selector: (#selector(updateTimer)), userInfo: nil, repeats: true)

WHERE
timeInterval:- Interval in which timer should fire like 1s, 10s, 100s etc. [Its value is in secs]
target:- function which pointed to class. So here I am pointing to current class.
selector:- function that will execute when timer fires.

func updateTimer(){
    //Implemetation 
} 

repeats:- true/false specifies that timer should call again n again.

Swift 2.2+ and Swift 3 Update

Use the new #selector expression, which eliminates the need to use string literals making usage less error-prone. For reference:

Selector("keyboardDidHide:")

becomes

#selector(keyboardDidHide(_:))

See also: Swift Evolution Proposal

Note (Swift 4.0):

If using #selectoryou would need to mark the function as @objc

Example:

@objc func something(_ sender: UIButton)


Since Swift 3.0 is published, it is even a little bit more subtle to declare a targetAction appropriate

class MyCustomView : UIView {

    func addTapGestureRecognizer() {

        // the "_" is important
        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(MyCustomView.handleTapGesture(_:)))
        tapGestureRecognizer.numberOfTapsRequired = 1
        addGestureRecognizer(tapGestureRecognizer)
    }

    // since Swift 3.0 this "_" in the method implementation is very important to 
    // let the selector understand the targetAction
    func handleTapGesture(_ tapGesture : UITapGestureRecognizer) {

        if tapGesture.state == .ended {
            print("TapGesture detected")
        }
    }
}

you create the Selector like below.
1.

UIBarButtonItem(
    title: "Some Title",
    style: UIBarButtonItemStyle.Done,
    target: self,
    action: "flatButtonPressed"
)

2.

flatButton.addTarget(self, action: "flatButtonPressed:", forControlEvents: UIControlEvents.TouchUpInside)

Take note that the @selector syntax is gone and replaced with a simple String naming the method to call. There’s one area where we can all agree the verbosity got in the way. Of course, if we declared that there is a target method called flatButtonPressed: we better write one:

func flatButtonPressed(sender: AnyObject) {
  NSLog("flatButtonPressed")
}

set the timer:

    var timer = NSTimer.scheduledTimerWithTimeInterval(1.0, 
                    target: self, 
                    selector: Selector("flatButtonPressed"), 
                    userInfo: userInfo, 
                    repeats: true)
    let mainLoop = NSRunLoop.mainRunLoop()  //1
    mainLoop.addTimer(timer, forMode: NSDefaultRunLoopMode) //2 this two line is optinal

In order to be complete, here’s the flatButtonPressed

func flatButtonPressed(timer: NSTimer) {
}

Also, if your (Swift) class does not descend from an Objective-C class, then you must have a colon at the end of the target method name string and you must use the @objc property with your target method e.g.

var rightButton = UIBarButtonItem(title: "Title", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("method"))

@objc func method() {
    // Something cool here   
} 

otherwise you will get a "Unrecognised Selector" error at runtime.


Selectors are an internal representation of a method name in Objective-C. In Objective-C "@selector(methodName)" would convert a source-code method into a data type of SEL. Since you can't use the @selector syntax in Swift (rickster is on point there), you have to manually specify the method name as a String object directly, or by passing a String object to the Selector type. Here is an example:

var rightBarButton = UIBarButtonItem(
    title: "Logout", 
    style: UIBarButtonItemStyle.Plain, 
    target: self, 
    action:"logout"
)

or

var rightBarButton = UIBarButtonItem(
    title: "Logout", 
    style: UIBarButtonItemStyle.Plain, 
    target: self, 
    action:Selector("logout")
)

Selector in Swift 4:

button.addTarget(self, action: #selector(buttonTapped(sender:)), for: UIControlEvents.touchUpInside)

Using #selector will check your code at compile time to make sure the method you want to call actually exists. Even better, if the method doesn’t exist, you’ll get a compile error: Xcode will refuse to build your app, thus banishing to oblivion another possible source of bugs.

override func viewDidLoad() {
        super.viewDidLoad()

        navigationItem.rightBarButtonItem =
            UIBarButtonItem(barButtonSystemItem: .Add, target: self,
                            action: #selector(addNewFireflyRefernce))
    }

    func addNewFireflyReference() {
        gratuitousReferences.append("Curse your sudden but inevitable betrayal!")
    }

It may be useful to note where you setup the control that triggers the action matters.

For example, I have found that when setting up a UIBarButtonItem, I had to create the button within viewDidLoad or else I would get an unrecognized selector exception.

override func viewDidLoad() {
    super.viewDidLoad() 

    // add button
    let addButton = UIBarButtonItem(image: UIImage(named: "746-plus-circle.png"), style: UIBarButtonItemStyle.Plain, target: self, action: Selector("addAction:"))
    self.navigationItem.rightBarButtonItem = addButton
}

func addAction(send: AnyObject?) {     
    NSLog("addAction")
}