@selector() in Swift? - TagMerge
5@selector() in Swift?@selector() in Swift?

@selector() in Swift?

Asked 9 months ago
955
5 answers

Swift itself doesn't use selectors — several design patterns that in Objective-C make use of selectors work differently in Swift. (For example, use optional chaining on protocol types or is/as tests instead of respondsToSelector:, and use closures wherever you can instead of performSelector: for better type/memory safety.)

But there are still a number of important ObjC-based APIs that use selectors, including timers and the target/action pattern. Swift provides the Selector type for working with these. (Swift automatically uses this in place of ObjC's SEL type.)

In Swift 2.2 (Xcode 7.3) and later (including Swift 3 / Xcode 8 and Swift 4 / Xcode 9):

You can construct a Selector from a Swift function type using the #selector expression.

let timer = Timer(timeInterval: 1, target: object,
                  selector: #selector(MyClass.test),
                  userInfo: nil, repeats: false)
button.addTarget(object, action: #selector(MyClass.buttonTapped),
                 for: .touchUpInside)
view.perform(#selector(UIView.insertSubview(_:aboveSubview:)),
             with: button, with: otherButton)

The great thing about this approach? A function reference is checked by the Swift compiler, so you can use the #selector expression only with class/method pairs that actually exist and are eligible for use as selectors (see "Selector availability" below). You're also free to make your function reference only as specific as you need, as per the Swift 2.2+ rules for function-type naming.

(This is actually an improvement over ObjC's @selector() directive, because the compiler's -Wundeclared-selector check verifies only that the named selector exists. The Swift function reference you pass to #selector checks existence, membership in a class, and type signature.)

There are a couple of extra caveats for the function references you pass to the #selector expression:

  • Multiple functions with the same base name can be differentiated by their parameter labels using the aforementioned syntax for function references (e.g. insertSubview(_:at:) vs insertSubview(_:aboveSubview:)). But if a function has no parameters, the only way to disambiguate it is to use an as cast with the function's type signature (e.g. foo as () -> () vs foo(_:)).
  • There's a special syntax for property getter/setter pairs in Swift 3.0+. For example, given a var foo: Int, you can use #selector(getter: MyClass.foo) or #selector(setter: MyClass.foo).

General notes:

Cases where #selector doesn't work, and naming: Sometimes you don't have a function reference to make a selector with (for example, with methods dynamically registered in the ObjC runtime). In that case, you can construct a Selector from a string: e.g. Selector("dynamicMethod:") — though you lose the compiler's validity checking. When you do that, you need to follow ObjC naming rules, including colons (:) for each parameter.

Selector availability: The method referenced by the selector must be exposed to the ObjC runtime. In Swift 4, every method exposed to ObjC must have its declaration prefaced with the @objc attribute. (In previous versions you got that attribute for free in some cases, but now you have to explicitly declare it.)

Remember that private symbols aren't exposed to the runtime, too — your method needs to have at least internal visibility.

Key paths: These are related to but not quite the same as selectors. There's a special syntax for these in Swift 3, too: e.g. chris.valueForKeyPath(#keyPath(Person.friends.firstName)). See SE-0062 for details. And even more KeyPath stuff in Swift 4, so make sure you're using the right KeyPath-based API instead of selectors if appropriate.

You can read more about selectors under Interacting with Objective-C APIs in Using Swift with Cocoa and Objective-C.

Note: Before Swift 2.2, Selector conformed to StringLiteralConvertible, so you might find old code where bare strings are passed to APIs that take selectors. You'll want to run "Convert to Current Swift Syntax" in Xcode to get those using #selector.

Source: link

92

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

Source: link

46

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.

Source: link

0

Before Swift 2.2, selectors were string literals and prone to error because we, as humans invented, and still contribute to typos whenever given the chance to write something without autocomplete.
let button = UIButton(type: .system)button.addTarget(self, action: Selector(“buttonTapped:”), for: .touchUpInside)...func buttonTapped(sender: UIButton) { }
A few other examples of how I like to name corresponding user interaction functions:
func segmentedControlValueChanged(sender: UISegmentedControl) { }func barButtonItemTapped(sender: UIBarButtonItem) { }func keyboardWillShowNotification(notification: Notification) { }
However, our selectors are now much safer in Swift 2.2, but it’s still U-G-L-Y (It ain’t got no alibi). And to have all these selectors littered throughout your codebase makes the matter worse. What if you have Massive View Controller? What if you use the same selector multiple times?
button.addTarget(self, action:      #selector(ViewController.buttonTapped(_:)), for: .touchUpInside)
This line is seriously way too long and kind of hard to read if you’re just scanning through. Imagine writing/copy-pasting that several times. Let’s tidy it up by placing all the selectors in one spot, where we can reference and edit them in a uniform matter.
fileprivate struct Action {    static let buttonTapped =         #selector(ViewController.buttonTapped(_:))}...button.addTarget(self, action: Action.buttonTapped,           for: .touchUpInside)
I had actually been using this for quite a few months now, and it has served me well for that time. But this morning I realised I could take this even further, and make it more …sugary. Why make an Action struct when we can make a Selector extension?
fileprivate extension Selector {    static let buttonTapped =         #selector(ViewController.buttonTapped(_:))}...button.addTarget(self, action: .buttonTapped, for: .touchUpInside)

Source: link

0

@objc func aMethod (_ firstParameter: Int, secondParameter: Int)
#selector (aController.aMethod (_:secondParameter:))
#selector (aController.aMethod)
@objc func aMethod (a:Int)
Fundamentally selectors don’t include the class name. You can supply a class name when using the
#selector

Source: link

Recent Questions on swift

    Programming Languages