Swift 5.7 problem with #selector syntax

The following code was compiling and working properly with Swift <= 5.6

let selector = #selector(URLSessionDataDelegate.urlSession(_:dataTask:didBecome:)! as (URLSessionDataDelegate) -> (URLSession, URLSessionDataTask, URLSessionDownloadTask) -> Void)

with new swift 5.7 it now fails with the following error:

Type of expression is ambiguous without more context

I have been trying different approaches but with no luck, how should I disambiguate it?

1 Like

Would traditional way of doing this work for you?

class Foo: NSObject, URLSessionDataDelegate {
    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didBecome downloadTask: URLSessionDownloadTask) {
        // ...
    }
}

From there you can further dispatch into your closure based approach if that's the end goal.

This is not related to selectors. You're just typing the closure incorrectly.

#selector(URLSessionDataDelegate.urlSession as (_) -> ((_, _, URLSessionDownloadTask) -> _)?)

Swift now supports references to optional methods on a protocol metatype, as well as references that are dynamically looked up on the AnyObject metatype. These references always have the type of a function that accepts a single argument and returns an optional value of function type:

1 Like

Thank you very much, it solved it.

1 Like

I definitely wouldn’t recommend omitting the labels here. There could be another delegate method with this signature someday.

I tried to give some thought to how this recommendation is different than, "never use implicit typing, because there is no way to mark overloads as frozen". Is it different? I don't recall hearing that guideline before.

A (non-generic) method is uniquely identified by the combination of its full name and argument types (…and return type, but let’s pretend people don’t do that). If you leave out the labels, you’re relying on nobody defining another method with the same base name and argument types; if you leave out the types, you’re relying on nobody defining another method with the same full name. Neither is futureproof but in practice for Cocoa-style delegate methods you’re more likely to see “different labels, same types” than you are to see “same labels, different types”.

1 Like

I think I disagree; compilation fails with Ambiguous use at the time a new overload is introduced.

E.g.

struct S {
  init(_: Int) { }
}

(0...1).map(S.init) // Fine until next year, when…
extension S {
  init(newOverload: Int) { }
}

What you're suggesting seems like a slippery slope towards elimination of implicit typing, to me, but I'm guessing you have some kind of guideline internalized that isn't so extreme. I just don't think I've heard it before.