Swift
Swift 3.0
Selector Syntax
Programming
Duplicate

Selector syntax for swift 3.0

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Swift 3.0 replaced string-based selectors with the #selector expression, giving compile-time checking for method references used in target-action patterns and notification observers. Before Swift 3, selectors were raw strings like "buttonTapped:" that would crash at runtime if misspelled. The #selector syntax catches these errors at compile time and auto-completes in Xcode. Any method referenced by #selector must be exposed to Objective-C with @objc.

Basic #selector Syntax

swift
1import UIKit
2
3class ViewController: UIViewController {
4
5    override func viewDidLoad() {
6        super.viewDidLoad()
7
8        let button = UIButton(type: .system)
9        button.setTitle("Tap Me", for: .normal)
10
11        // Swift 3.0+ selector syntax
12        button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
13
14        view.addSubview(button)
15    }
16
17    // Must be @objc to be used with #selector
18    @objc func buttonTapped() {
19        print("Button was tapped")
20    }
21}

The #selector(buttonTapped) expression resolves to a Selector value at compile time. If buttonTapped does not exist or is not marked @objc, the compiler emits an error.

Selectors with Parameters

swift
1class ViewController: UIViewController {
2
3    override func viewDidLoad() {
4        super.viewDidLoad()
5
6        // Method with one parameter
7        let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
8        view.addGestureRecognizer(tap)
9
10        // Timer with selector
11        Timer.scheduledTimer(timeInterval: 1.0,
12                             target: self,
13                             selector: #selector(timerFired(_:)),
14                             userInfo: nil,
15                             repeats: true)
16    }
17
18    @objc func handleTap(_ sender: UITapGestureRecognizer) {
19        print("Tapped at \(sender.location(in: view))")
20    }
21
22    @objc func timerFired(_ timer: Timer) {
23        print("Timer fired")
24    }
25}

When the method takes a parameter, include the argument label in the #selector expression. #selector(handleTap(_:)) matches func handleTap(_ sender: UITapGestureRecognizer).

Disambiguating Overloaded Methods

swift
1class MyClass: NSObject {
2
3    @objc func process() {
4        print("No arguments")
5    }
6
7    @objc func process(data: String) {
8        print("String: \(data)")
9    }
10
11    @objc func process(data: Int) {
12        print("Int: \(data)")
13    }
14
15    func setupSelectors() {
16        // Disambiguate by specifying the full signature
17        let sel1 = #selector(process as () -> Void)
18        let sel2 = #selector(process(data:) as (String) -> Void)
19        let sel3 = #selector(process(data:) as (Int) -> Void)
20
21        print(sel1, sel2, sel3)
22    }
23}

When multiple methods share the same base name, cast to the specific function signature to tell the compiler which overload you mean.

Selectors for Properties (Getter/Setter)

swift
1class Person: NSObject {
2    @objc var name: String = ""
3
4    func demo() {
5        // Getter selector
6        let getter = #selector(getter: Person.name)
7
8        // Setter selector
9        let setter = #selector(setter: Person.name)
10
11        print(getter)  // "name"
12        print(setter)  // "setName:"
13    }
14}

Use #selector(getter:) and #selector(setter:) to reference property accessors, commonly used with KVO and key-value observing APIs.

Notification Observers

swift
1class ViewController: UIViewController {
2
3    override func viewDidLoad() {
4        super.viewDidLoad()
5
6        NotificationCenter.default.addObserver(
7            self,
8            selector: #selector(keyboardWillShow(_:)),
9            name: UIResponder.keyboardWillShowNotification,
10            object: nil
11        )
12    }
13
14    @objc func keyboardWillShow(_ notification: Notification) {
15        guard let frame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else {
16            return
17        }
18        print("Keyboard height: \(frame.height)")
19    }
20
21    deinit {
22        NotificationCenter.default.removeObserver(self)
23    }
24}

Migration from Swift 2 to Swift 3

swift
1// Swift 2 (deprecated)
2button.addTarget(self, action: Selector("buttonTapped"), for: .touchUpInside)
3// or
4button.addTarget(self, action: "buttonTapped", for: .touchUpInside)
5
6// Swift 3+ (correct)
7button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)

String-based selectors compile but produce a deprecation warning in Swift 3 and are removed in later versions. Always use #selector for compile-time safety.

@objc Requirement

swift
1class ViewController: UIViewController {
2
3    // This works — exposed to Objective-C
4    @objc func validSelector() { }
5
6    // This does NOT work with #selector
7    func swiftOnlyMethod() { }
8
9    // Private @objc methods work fine
10    @objc private func privateAction() { }
11
12    func setup() {
13        // Compiles
14        let sel1 = #selector(validSelector)
15
16        // Compiler error: argument of #selector refers to instance method
17        // that is not exposed to Objective-C
18        // let sel2 = #selector(swiftOnlyMethod)
19
20        // Compiles — @objc private is valid
21        let sel3 = #selector(privateAction)
22    }
23}

Every method referenced by #selector must be @objc. In Swift 4+, methods are not implicitly @objc even if the class inherits from NSObject, so the annotation is always required.

Common Pitfalls

  • Forgetting @objc: #selector only works with methods exposed to Objective-C. Omitting @objc produces a compile error. In Swift 4+, inheriting from NSObject alone is not enough.
  • Wrong argument labels: #selector(handleTap) fails if the method is func handleTap(_ sender: UITapGestureRecognizer). You need #selector(handleTap(_:)) with the argument label.
  • Using string selectors: Selector("methodName") bypasses compile-time checking and crashes at runtime if the method does not exist. Always use #selector.
  • Ambiguous overloads without cast: When multiple methods share a name, #selector(process) is ambiguous. Disambiguate with #selector(process as () -> Void).
  • Structs and enums: #selector requires Objective-C compatibility, which means the containing type must be a class (inheriting from NSObject). Structs and enums cannot use #selector.

Summary

  • Use #selector(methodName) for compile-time checked selectors in Swift 3+
  • Include argument labels for methods with parameters: #selector(handleTap(_:))
  • Disambiguate overloads with type casts: #selector(method as (String) -> Void)
  • Use #selector(getter:) and #selector(setter:) for property accessors
  • All selector targets must be marked @objc — Swift 4+ does not infer this
  • Never use string-based Selector("...") — it bypasses compile-time safety

Course illustration
Course illustration

All Rights Reserved.