Understanding Swift 2.2 Selector Syntax - #Selector()

Understanding Swift 2.2 Selector Syntax - #selector()

The bit in parenthesis is a mechanism for identifying the argument list for the selector that you want.

I recommend you look at the Generalized Naming proposal from Swift Evolution. It covers cases where you have a number of functions that differ only by their parameter labels and need to refer to them. The example from that document is:

extension UIView {
func insertSubview(view: UIView, at index: Int)
func insertSubview(view: UIView, aboveSubview siblingSubview: UIView)
func insertSubview(view: UIView, belowSubview siblingSubview: UIView)
}

If you wanted to get a function value for one of those the result is ambiguous:

let fn = someView.insertSubview // ambiguous: could be any of the three methods

The solution implemented is to add the argument labels, without any type information to the code that generates the function value to disambiguate which you want:

let fn = someView.insertSubview(_:at:)
let fn1 = someView.insertSubview(_:aboveSubview:)

See how the labels are added in the parens?

This proposal played a role in the one that most directly applies to your question:

Referencing the Objective-C selector of a method

In this particular case the selector you want to refer to is timerCalled: which is a function of one parameter that has no label. Hence (_:). The underscore means the label is not specified and the colon.

Swift 2.2 string literal selector

It seems to be a combination of several problems:

  • The collation must be created with UILocalizedIndexedCollation.currentCollation().
  • The object class needs an instance method returning a String.
  • The #selector must refer to that instance method, both #selector(<Type>.<method>) and #selector(<instance>.<method>) can be used.

Here is a self-contained example which seems to work as expected:

class Person : NSObject {
let firstName : String
let lastName : String

init(firstName : String, lastName : String) {
self.firstName = firstName
self.lastName = lastName
}

func lastNameMethod() -> String {
return lastName
}
}

and then

let person = Person(firstName: "John", lastName: "Doe")
let collation = UILocalizedIndexedCollation.currentCollation()
let section = collation.sectionForObject(person, collationStringSelector: #selector(Person.lastNameMethod))
print(section) // 3

Here, both #selector(Person.lastNameMethod) and #selector(person.lastNameMethod) can be used.

Use of unresolved identifier using the new Swift 2.2 syntax for selectors on implicit setter

The issue here is that you're working with a property, not a method. This has two problems:

  • The ObjC setter/getter method pair for a property is generated at run time.
  • The #selector expression requires a Swift function/method reference.

When Swift compiles your source, it doesn't know that the ObjC -(UITextField*)activeTextField and -(void)setActiveTextField:(UITextField*)field methods will exist, so it can't generate function references for them. Since it can't generate a function reference, it doesn't have something it can use for the #selector expression.

For now, there isn't a way to use #selector to access property getters/setters — using Selector(...) with a string constant is your only option.

(This is actually just a new face on a longstanding known issue... it's the same reason you also can't pass a property getter/setter to something like map or filter. I think I've seen something on http://bugs.swift.org about this, but I'm not finding it at the moment.)

swift 2.2 #selector on var

You have a misunderstanding of what can be passed into #selector(). You should definitely read the StackOverflow questions that Eric D posted:

Overview on what you can use #selector() for: Understanding Swift 2.2 Selector Syntax - #selector()

The reason why you can not pass a Swift property in to #selector():

Selector availability: The method referenced by the selector must be
exposed to the ObjC runtime. This is already the case if it's in a
class that (ultimately) inherits from NSObject, but if it's in a pure
Swift class you'll need to preface that method's declaration with
@objc. Remember that private symbols aren't exposed to the runtime,
too — your method needs to have at least internal visibility.

- Referenced from @selector() in Swift?

In short, you can't pass in a var and you need to pass a function that the Objective-C runtime is aware of. Either it is an Objective-C method, or it is a Swift method marked as @objc.

Pass on Argument in #selector Swift 2.2

creating a generic UITapGestureRecognizer instead use this:

class CustomTapGestureRecognizer: UITapGestureRecognizer {
var channelId: String?
}

also use this:

override func viewDidLoad() {
super.viewDidLoad()

let gestureRecognizer = CustomTapGestureRecognizer(target: self, action: #selector(tapped(_:))
gestureRecognizer.channelId = "Your string"
view1.addGestureRecognizer(gestureRecognizer)
}

func tapped(gestureRecognizer: CustomTapGestureRecognizer) {
if let channelId = gestureRecognizer.channelId {
//print
}
}

@selector() in Swift?

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.

Syntax Selector Swift 2.2

@Eendje's answer is incorrect by its first comment.

I think it is better answer.

NSNotificationCenter.defaultCenter().addObserver(self, #selector(self.goBack), name: "your notification name", object: nil)

If some actions has target, it should be presented like #selector(target.method) or #selector(target.method(_:))

Here is another example

UIGestureRecognizer(target: target action:#selector(target.handleGesture(_:))

Swift 2.2 Selector with multiple arguments - actually passing in

You cannot pass parameters to selectors, selector is just a method name, nothing else. You are not calling the method so you cannot pass parameters.
Just call the necessary code inside your tap handler.

func onTap() {
MainViewController.showViewWithIdentifier(exploreView, id:"explore")
}

and then

UITapGestureRecognizer(target: self, action: #selector(onTap))


Related Topics



Leave a reply



Submit