What is @selector() in Swift and why is it needed?
If you've ever worked with Objective-C or encountered legacy code, you've probably heard of the magical @selector() construct. In Swift, it has transformed into #selector, but the essence remains the same: it's a bridge between Swift's static typing and the dynamic nature of the Objective-C runtime.
Imagine you need to pass a reference to a method as an argument. For example, to handle a button press or subscribe to notifications. In Swift, we are used to using closures, but Apple's libraries (UIKit, AppKit, Foundation) still rely on the good old selector mechanism. This is where @selector() in Swift becomes an indispensable tool.
In this guide, we'll cover:
- How to properly declare selectors in modern Swift
- Common mistakes and how to avoid them
- Real-world examples from iOS development
- When to use
#selectorand when to use alternatives
From @selector() to #selector: The Evolution of Syntax
In Objective-C, a selector looks like this:
[@selector(buttonTapped:)]In Swift 2.0, the #selector construct appeared and became the standard. Here's what it looks like now:
#selector(MyClass.buttonTapped(_:))The key difference: #selector in Swift is checked by the compiler. If you misspell the method name or pass an incorrect signature, the compiler will throw an error. This is a huge advantage over Objective-C, where a selector is just a string and the error only surfaces at runtime.
Rules for Declaring Methods for #selector
The method you pass to #selector must be:
- Accessible from Objective-C — marked with the
@objcattribute - Not private (or marked as
@objc private) - Signature-compatible with what the calling side expects
Example of a correct declaration:
class ViewController: UIViewController { @objc func buttonTapped(_ sender: UIButton) { print("Button tapped!") } override func viewDidLoad() { super.viewDidLoad() let button = UIButton(type: .system) button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside) }}Note the @objc before the method. Without it, the compiler will not allow using #selector.
@selector() Swift: Practical Examples
1. Target-Action: Buttons and Gestures
The most common use case for #selector is binding actions to UI elements:
class ProfileViewController: UIViewController { @objc private func avatarTapped(_ gesture: UITapGestureRecognizer) { print("Avatar tapped") } @objc private func settingsButtonPressed() { print("Open settings") } override func viewDidLoad() { super.viewDidLoad() // Gesture let tapGesture = UITapGestureRecognizer(target: self, action: #selector(avatarTapped(_:))) avatarImageView.addGestureRecognizer(tapGesture) // Button settingsButton.addTarget(self, action: #selector(settingsButtonPressed), for: .touchUpInside) }}Remember: if the method accepts a parameter (e.g., UITapGestureRecognizer), you must specify _ in the selector signature: #selector(avatarTapped(_:)).
2. PerformSelector and Delayed Calls
Sometimes you need to call a method dynamically, especially when working with KVO or timers:
class TimerManager { @objc func fireTimer() {