How to Resolve "Ambiguous Use Of" Compile Error With Swift #Selector Syntax

How do I resolve ambiguous use of compile error with Swift #selector syntax?

[NOTE This answer was originally formulated under Swift 2.2. It has been revised for Swift 4, involving two important language changes: the first method parameter external is no longer automatically suppressed, and a selector must be explicitly exposed to Objective-C.]

You can work around this problem by casting your function reference to the correct method signature:

let selector = #selector(test as () -> Void)

(However, in my opinion, you should not have to do this. I regard this situation as a bug, revealing that Swift's syntax for referring to functions is inadequate. I filed a bug report, but to no avail.)


Just to summarize the new #selector syntax:

The purpose of this syntax is to prevent the all-too-common runtime crashes (typically "unrecognized selector") that can arise when supplying a selector as a literal string. #selector() takes a function reference, and the compiler will check that the function really exists and will resolve the reference to an Objective-C selector for you. Thus, you can't readily make any mistake.

(EDIT: Okay, yes you can. You can be a complete lunkhead and set the target to an instance that doesn't implement the action message specified by the #selector. The compiler won't stop you and you'll crash just like in the good old days. Sigh...)

A function reference can appear in any of three forms:

  • The bare name of the function. This is sufficient if the function is unambiguous. Thus, for example:

    @objc func test(_ sender:AnyObject?) {}
    func makeSelector() {
    let selector = #selector(test)
    }

    There is only one test method, so this #selector refers to it even though it takes a parameter and the #selector doesn't mention the parameter. The resolved Objective-C selector, behind the scenes, will still correctly be "test:" (with the colon, indicating a parameter).

  • The name of the function along with the rest of its signature. For example:

    func test() {}
    func test(_ sender:AnyObject?) {}
    func makeSelector() {
    let selector = #selector(test(_:))
    }

    We have two test methods, so we need to differentiate; the notation test(_:) resolves to the second one, the one with a parameter.

  • The name of the function with or without the rest of its signature, plus a cast to show the types of the parameters. Thus:

    @objc func test(_ integer:Int) {}
    @nonobjc func test(_ string:String) {}
    func makeSelector() {
    let selector1 = #selector(test as (Int) -> Void)
    // or:
    let selector2 = #selector(test(_:) as (Int) -> Void)
    }

    Here, we have overloaded test(_:). The overloading cannot be exposed to Objective-C, because Objective-C doesn't permit overloading, so only one of them is exposed, and we can form a selector only for the one that is exposed, because selectors are an Objective-C feature. But we must still disambiguate as far as Swift is concerned, and the cast does that.

    (It is this linguistic feature that is used — misused, in my opinion — as the basis of the answer above.)

Also, you might have to help Swift resolve the function reference by telling it what class the function is in:

  • If the class is the same as this one, or up the superclass chain from this one, no further resolution is usually needed (as shown in the examples above); optionally, you can say self, with dot-notation (e.g. #selector(self.test), and in some situations you might have to do so.

  • Otherwise, you use either a reference to an instance for which the method is implemented, with dot-notation, as in this real-life example (self.mp is an MPMusicPlayerController):

    let pause = UIBarButtonItem(barButtonSystemItem: .pause, 
    target: self.mp, action: #selector(self.mp.pause))

    ...or you can use the name of the class, with dot-notation:

    class ClassA : NSObject {
    @objc func test() {}
    }
    class ClassB {
    func makeSelector() {
    let selector = #selector(ClassA.test)
    }
    }

    (This seems a curious notation, because it looks like you're saying test is a class method rather than an instance method, but it will be correctly resolved to a selector nonetheless, which is all that matters.)

Ambiguous use of 'connection(_:didReceive:)'

solved, just add "?":

    static let didReceiveResponseSelector : Selector = #selector((NSURLConnectionDataDelegate.connection(_:didReceive:)) as ((NSURLConnectionDataDelegate) -> (NSURLConnection,URLResponse) -> void)?)

Ambiguous use of 'pow'

Either use Double

pow(10.0, 9.0)

Or use an Int value directly

func gigaSecond( second: Int)-> Int {
return second / 1_000_000_000
}

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.

How can I resolve type of expression is ambiguous error?

The error is a bit misleading. The do - catch block around the try statement is missing.

do {
let remoteUser = try User(record: postDict, context: DatabaseManager.shared.persistentContainer.newBackgroundContext()
self.fetchShowroom(forIdentifier: showroomReference) { (result) in
do {
let showroom = try result.value()
remoteUser.showroom = showroom
remoteUser.brands = showroom.brands
completionHandler(.success(remoteUser))
} catch {
completionHandler(.failure(error))
}
}
} catch {
completionHandler(.failure(error))
}

How do I use #selector?

In Swift 3, it looks like this:

NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)

Error occurred after code migration from swift 2.0 to swift 3.0

UIViewAnimating already has a method to start the animation after a delay, you should use this one instead :

startAnimation(afterDelay: 0.3)

If you insist on using perform(_:with:afterDelay:) you can use it like so:

perform(NSSelectorFromString("startAnimation"), with: nil, with: 0.3)

But keep in mind that you loose compiler safety with this approach, and if you make a typo in your selector your app will crash.

Also, your code is weird because UIViewController doesn't conform to UIViewAnimating, so unless you implemented the protocol yourself (which would be quite unusual), it's not going to work. It's more likely that you have a method called startAnimation in your view controller and that the Swift migrator mistakenly added the reference to UIViewAnimating. In which case the correct code would be:

perform(#selector(self.startAnimation), with: nil, afterDelay: 0.3)


Related Topics



Leave a reply



Submit