Skip to content

Learn

Swift selectors: Everything you need to know

Learn what Objective-C selectors are, how they work in Swift, and when to use them—plus how Tricentis Testim Mobile helps test iOS apps at scale.

swift selectorsAs iOS applications grow more complex, developers are increasingly expected to balance modern Swift development with legacy Objective-C interoperability. While Swift has significantly reduced the need for older runtime concepts, some Objective-C mechanisms—like selectors—still play an important role in real-world iOS development.

Selectors are one of those concepts that many Swift developers encounter only when they have to interact with UIKit, Cocoa APIs, or system-level features such as timers, gesture recognizers, and notifications. Understanding how selectors work—and when to use them correctly—can help you avoid runtime errors and build more predictable applications.

At the same time, ensuring that these interactions behave correctly across devices and OS versions requires strong testing practices. Tricentis Testim Mobile complements native iOS development by providing AI-powered mobile test automation that helps teams validate event-driven behavior—like selectors, notifications, and UI actions—at scale.

In this article, we’ll explore Objective-C selectors, explain how they work, why they still exist in Swift, and walk through common selector use cases with practical examples.

What is a Selector?

Apple defines a selector as “a type that refers to the name of an Objective-C method.” It adds that “in Swift, Objective-C selectors are represented by the Selector structure, and you create them using the #selector expression.”

Now, this doesn’t do a great job clarifying what it is. So, here’s a more down-to-earth explanation: In simple terms, a selector is a string that represents the signature of a generic method in a class.

Strongly typed languages won’t let you run code with a type mismatch or call to a method that doesn’t exist. However, Objective-c allows you to create objects without the class reference or call methods by knowing the method name even if Objective-c does not know the object class beforehand. This last feature is what selectors accomplish. You can send generic messages to objects without knowing their type.

Strongly typed languages won’t let you run code with a type mismatch or call to a method that doesn’t exist

Objective-c Selector

Now, how does this work? In essence, every time you send a message to an Objective-C object, it is compiled into a call to an Objective-C runtime function called objc_msgSend().

This function takes several arguments:

  • self: The object you sent the message to on the call.
  • cmd: A selector representing the message you sent and the method you intended to call.
  • arg: The remaining arguments of the initial method call.

To illustrate, here’s some Objective-C code:

[myObject doCalculation: @"TestText" and: 30];

That code would become the following in C:

objc_msgSend(myObject, “doCalculation:and:”, @“TestText”, 30);

This ‘objc_msgSend’ function uses the pointer ‘myObject’ to identify the class ‘myObject’. Once it’s found, the runtime searches for the method implementation in the class corresponding to the message represented in the selector and calls it.

When using selectors, you must be careful not to send a message to an object that doesn’t have it defined. It’s vital to ensure that the message target is an instance of a class (or a class object) that implements a method corresponding to the selector. Otherwise, you end up with an unrecognized selector exception.

Selectors (and protocols for that matter) allow controllers to manage and observe themselves and other controllers. Additionally, it allows for easy state data manipulation between controllers. This is one of the hallmarks of the power and dynamic nature of Objective-c.

Selectors in Swift

As you probably know, Swift is a strongly typed language, and it won’t let you call a method on an object that doesn’t implement it or inherits it from a superclass or extension.

But even in a pure Swift project, there are some cases where using selectors is necessary to access some of the native interfaces in cocoa, like the target/action pattern to UI controls found on the Timer and UIBarButtonItem elements. Additionally, you have to use selectors if you need to register a notification observer with NSNotificationCenter. This process is necessary because you have to tell the notification center what method to call and on what listener object to call it on when the user interacts with the control or the system posts the notification.

Let’s illustrate with an example.

The following code contains a simple timer that requires you to provide the operation that Swift should perform once the specified interval ends.

import UIKit
let object = MyClass()
Timer.scheduledTimer(timeInterval: 1,
                                 	target: object,
                                 	selector: #selector(MyClass.sayHi),
                                 	userInfo: nil,
                                 	repeats: true)
class MyClass {
    @objc
    func sayHi() {
        print("Hi there!")
    }
}

Notice that the target and selector parameters contain an instance of the object containing the method with the operation and the selector specifying the method itself.

In Swift, you use the ‘#selector’ directive to create Objective-c selector strings from strongly typed class objects. This method allows you to retain the convenience and security of types while safely using the selectors.

Additionally, to reference the class method in the selector, you must label it with the ‘@objc’ directive, which makes it visible to Objective-c.

Swift is a strongly typed language, and it won’t let you call a method on an object that doesn’t implement it or inherits it from a superclass or extension.

Parameters in Selectors

OK, but what about parameters?

That’s a bit more complicated. In the particular case of the Timer, you can use the ‘userinfo’ parameter to pass data to the target method.

import UIKit
let object = MyClass()
Timer.scheduledTimer(timeInterval: 1,
                     				target: object,
                     				selector: #selector(MyClass.sayHiTo),
                    		 		userInfo: ["name" : "Juan",
                                					"age" : 34],
                     				repeats: true)
class MyClass {
    @objc
    func sayHiTo(sender: Timer) {
        let data: [String : AnyObject] = sender.userInfo! as! [String : AnyObject]
        let name: String = data["name"] as! String
        let age: Int = data["age"] as! Int
        print("Hi there \(name)! I know you are \(age) years old now.")
    }
}

This process is cumbersome and verbose, but it does the job.

However, all this is pointless because you can easily create a timer with a closure.

import UIKit
let name = "Juan"
let age = 34
Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { sender in
    print("Hi there \(name)! I know you are \(age) years old now.")
}

Oh well.

Nevertheless, selectors have been on a steady decline since the earliest versions of Swift didn’t include some core selector functionality.

Common Selectors in Swift

Here are some common use cases for selectors in Swift and how to implement them properly.

TapGesture

import UIKit
let object = MyClass()
let tapGesture = CustomTapGestureRecognizer(target: object,
                                            action: #selector(MyClass.sayHiTo(sender:)))
tapGesture.myname = "Juan"
class MyClass {
    @objc
    func sayHiTo(sender: CustomTapGestureRecognizer) {
        let name = sender.myname ?? "No Name"
        print("Hi there \(name)!")
    }
}
class CustomTapGestureRecognizer: UITapGestureRecognizer {
    var myname: String?
}

For this particular example, it was necessary to add a class that has additional properties defined to be able to pass parameters to the target operation. However, it all works essentially the same.

UIButton

Here you can see that the implementation is quite streamlined and straightforward. However, keep in mind that this is a UIKit element and not the SwiftUI button element, which doesn’t use selectors but closures.

NotificationCenter

import UIKit
let object = MyClass()
NotificationCenter.default.addObserver(object,
                                       selector: #selector(MyClass.sayHiTo),
                                       name: Notification.Name("MyNotificationName"),
                                       object: nil)
class MyClass {
    @objc
    func sayHiTo(sender: Notification) {
        print("Hi there!")
    }
}

In this example, you only need to pass the object reference, the selector, and the notification name. Notice that the parameter in the target method changed to match the sender type. This will help you retrieve additional information passed to the method.

Moving on

Swift’s evolution has dramatically reduced the need for Objective-C selectors, offering a cleaner, safer, and more expressive language. For developers coming from Objective-C, Java, or C, this shift represents a major improvement in reliability and maintainability.

That said, selectors still exist—and understanding them remains essential when working with UIKit, system APIs, or legacy codebases. Knowing how and when to use selectors ensures fewer runtime crashes and more predictable application behavior.

To guarantee that selector-driven interactions, notifications, and UI events work reliably across devices, teams need a robust testing workflow. This is where Tricentis Testim Mobile fits naturally into modern iOS development. With AI-powered mobile test automation and support for both coded and no-code testing, Tricentis Testim Mobile helps teams validate real user flows without adding engineering overhead.

If you want your iOS applications to be stable, scalable, and production-ready—without slowing down your team—Tricentis Testim Mobile provides the confidence and coverage needed to ship faster and smarter.

Learn more about Tricentis Testim Mobile and how it simplifies mobile testing at scale.

Author:

Guest Contributors

Date: Jan. 22, 2026
Author:

Guest Contributors

Date: Jan. 22, 2026

You may also be interested in...