Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-3062] Ambiguous selector in Swift 3 for CoreBluetooth delegate methods #45652

Closed
swift-ci opened this issue Oct 27, 2016 · 26 comments
Closed
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler itself optional protocol requirements Feature → protocol: optional protocol requirements selectors Feature: Objective-C selectors in Swift (#selector) swift 3.0 type checker Area → compiler: Semantic analysis

Comments

@swift-ci
Copy link
Contributor

swift-ci commented Oct 27, 2016

Previous ID SR-3062
Radar None
Original Reporter Julien Coudsi (JIRA User)
Type Bug

Attachment: Download

Environment

Xcode 8.1, Swift 3

Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug, DiagnosticsQoI, TypeChecker
Assignee None
Priority Medium

md5: 36322bb25de0649b7703eec4d6a684e0

Issue Description:

I need to declare a selector on CBPeripheralDelegate functions, for example :

func peripheral(peripheral: CBPeripheral, didUpdateValueForCharacteristic characteristic: CBCharacteristic, error: NSError?)

In Swift3, it is renamed to peripheral(_: didUpdateValueFor:error), and it is same as the func peripheral(peripheral: CBPeripheral, didUpdateValueForDescriptor descriptor: CBDescriptor, error: NSError?)

So, when I try to define a selector like this :

#selector(CBPeripheralDelegate.peripheral(_:didUpdateValueFor:error:))

It will cause a compile error: ambiguous use.

So I try to define with the parameters types :

#selector(((CBPeripheralDelegate.peripheral(_:didUpdateValueFor:error:)) as (CBPeripheralDelegate) -> (CBPeripheral, CBCharacteristic, NSError?) -> Void)

I try also :

#selector(((CBPeripheralDelegate.peripheral(_:didUpdateValueFor:error:)) as (CBPeripheralDelegate) -> (CBPeripheral, CBCharacteristic, NSError) -> Void)

But it failed either.

@belkadan
Copy link
Contributor

cc @DougGregor

@belkadan
Copy link
Contributor

Julien Coudsi (JIRA User), can I ask why you actually need the #selector? If it's for respondsToSelector purposes, Swift can do that anyway by getting the function without calling it. (Admittedly you need to specify the type to disambiguate in a similar ugly fashion.)

@swift-ci
Copy link
Contributor Author

Comment by Julien Coudsi (JIRA)

@belkadan It is to use a DelegateProxy with RxSwift, which needs to intercept delegates method by a selector to transform it to an observer.

@swift-ci
Copy link
Contributor Author

swift-ci commented Nov 9, 2016

Comment by Julien Coudsi (JIRA)

@belkadan @DougGregor Do you have an idea about it ?
Thanks

@belkadan
Copy link
Contributor

belkadan commented Nov 9, 2016

It looks like the 'NSError' parameter is imported as 'Error', and that's what's throwing off your workaround.

optional public func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?)

At the very least the error messages could certainly be better here (although I'm guessing the full version in the Log Navigator would have had useful notes). Being able to write the types inline is also a reasonable request, though that would have to go through the Swift Evolution Process and may be more work than is worth it for a relatively marginal feature.

@swift-ci
Copy link
Contributor Author

swift-ci commented Nov 9, 2016

Comment by Julien Coudsi (JIRA)

@belkadan

Well seen, it's an Error type. But I tried with "Error" and the problem is the same.
Here is the log from the log navigator :

error: ambiguous reference to member 'peripheral(_:didUpdateValueFor:error:)'
        let selector = #selector(((CBPeripheralDelegate.peripheral(_:didUpdateValueFor:error:)) as (CBPeripheralDelegate) -> (CBPeripheral, CBCharacteristic, Error?) -> Void))
                                   ^~~~~~~~~~~~~~~~~~~~
CoreBluetooth.CBPeripheralDelegate:98:26: note: found this candidate
    optional public func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?)
                         ^
CoreBluetooth.CBPeripheralDelegate:143:26: note: found this candidate
    optional public func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor descriptor: CBDescriptor, error: Error?)

I don't understand by the compiler fails to match the good function, because the parameter types are different and specified in the selector...

@belkadan
Copy link
Contributor

belkadan commented Nov 9, 2016

Oof. Turns out the method being optional is significant. This works for me:

let selector = #selector(CBPeripheralDelegate.peripheral(_:didUpdateValueFor:error:) as ((CBPeripheralDelegate) -> (CBPeripheral, CBCharacteristic, Error?) -> Void)?)

@swift-ci
Copy link
Contributor Author

swift-ci commented Nov 9, 2016

Comment by Julien Coudsi (JIRA)

Perfect, it works for me, Thanks a lot 🙂
It is a bug ? The "optional" keyword for a delegate method is different of the "?", which means "can be nil", and not "can be unimplemented"

@belkadan
Copy link
Contributor

belkadan commented Nov 9, 2016

Since we expose optional methods as really being optional on protocol values, I think it's correct, just inconvenient (and poorly-diagnosed).

someDelegate.peripheral?(device, didUpdateValueFor: characteristic, error: nil)

if let callback = someDelegate.peripheral(_:didUpdateValueFor:error:) as{  }

@swift-ci
Copy link
Contributor Author

swift-ci commented Nov 9, 2016

Comment by Julien Coudsi (JIRA)

Thanks for your answer and for your explanations 🙂

@swift-ci
Copy link
Contributor Author

swift-ci commented Oct 24, 2017

Comment by Bob Godwin (JIRA)

This problem is back in Swift 4 I am having this same issue in with Xcode 9.0.1 this is the method I am trying to wrap. WKNavigationDelegate.webView(_:decidePolicyFor:decisionHandler🙂 The naming clashes because there are 2 methods with same name.

let sel = #selector((WKNavigationDelegate.webView(_:decidePolicyFor:decisionHandler🙂 as (WKNavigationDelegate) -> (WKWebView) -> (WKWebView, WKNavigationResponse, @escaping(WKNavigationResponsePolicy) -> Swift.Void ) -> Swift.Void ))

Can you guys help or I should just give up on this?

@belkadan
Copy link
Contributor

Well, the workaround above still works: wrap the function type in Optional to go with the requirement itself being optional. And we'd still like to improve it at some point.

@swift-ci
Copy link
Contributor Author

Comment by Bob Godwin (JIRA)

@belkadan Sorry to disappoint you but it doesn't work![]( I have written this code in all forms and it kept on banging on same error. Tried the example above and it did not work) For goodness sake how can Apple be so careless to have this kind of naming conflicts. This is total negligence. Please go into this delegate WKNavigationDelegate and copy paste the how to create the `decisionHandleFor` related functions You can try it yourself with optional and everything you want but it doesn't work. Fix it Apple!!!!

@belkadan
Copy link
Contributor

belkadan commented Oct 25, 2017

Works fine for me:

import WebKit

func test() {
  let sel = #selector(WKNavigationDelegate.webView(_:decidePolicyFor:decisionHandler:) as ((WKNavigationDelegate) -> (WKWebView, WKNavigationResponse, @escaping(WKNavigationResponsePolicy) -> Void) -> Void)?)
}

Your example above does have an extra "(WKWebView) ->" in it. I suppose the diagnostics could be improved there, but we're already in edge-case-land; mocking out selectors like this is an ObjCism that isn't really a recommended pattern in Swift.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
@freak4pc
Copy link
Contributor

freak4pc commented Jun 7, 2022

Sorry for the huge bump but seems like this just became an issue again in Xcode 14. Should I just open a new bug for it?

The workaround no longer works, too.

image

@kennyevo
Copy link

kennyevo commented Jun 7, 2022

Sorry for the huge bump but seems like this just became an issue again in Xcode 14. Should I just open a new bug for it?

The workaround no longer works, too.

image

Trying to figure out the same issue, let me know if you find a workaround :D

@r-peck
Copy link

r-peck commented Jun 7, 2022

@freak4pc @kennyevo It looks like getting the function as a value for an optional protocol method changed from being an optional function value to being a non-optional function that takes the instance and returns the instance function as an optional, so the position of the parentheses has to change. The following compiles for me (previous version shown commented out for comparison):

static let decidePolicyNavigationResponse: Selector = 
// #selector(WKNavigationDelegate.webView(_:decidePolicyFor:decisionHandler:) as ((WKNavigationDelegate) -> (WKWebView, WKNavigationResponse, @escaping (WKNavigationResponsePolicy) -> Void) -> Void)?)
   #selector(WKNavigationDelegate.webView(_:decidePolicyFor:decisionHandler:) as (WKNavigationDelegate) -> ((WKWebView, WKNavigationResponse, @escaping (WKNavigationResponsePolicy) -> Void) -> Void)?)
static let decidePolicyNavigationAction: Selector = 
// #selector(WKNavigationDelegate.webView(_:decidePolicyFor:decisionHandler:) as ((WKNavigationDelegate) -> (WKWebView, WKNavigationAction, @escaping(WKNavigationActionPolicy) -> Void) -> Void)?)
   #selector(WKNavigationDelegate.webView(_:decidePolicyFor:decisionHandler:) as (WKNavigationDelegate) -> ((WKWebView, WKNavigationAction, @escaping(WKNavigationActionPolicy) -> Void) -> Void)?)

@AnthonyLatsis
Copy link
Collaborator

It looks like getting the function as a value for an optional protocol method changed from being an optional function value to being a non-optional function that takes the instance and returns the instance function as an optional

Yes, this is mentioned in the change log. Unbound references to optional protocol methods should be of a non-optional function type that takes an instance and returns an optional function type, i.e. (P) -> ((args) -> result)?. It was a bug that the entire type was getting wrapped in an optional. Here's a related twitter thread.

@freak4pc
Copy link
Contributor

freak4pc commented Jun 8, 2022

#selector(WKNavigationDelegate.webView(_:decidePolicyFor:decisionHandler:) as (WKNavigationDelegate) -> ((WKWebView, WKNavigationAction, @escaping(WKNavigationActionPolicy) -> Void) -> Void)?)

So would this have to live inside a compiler directive? This change doesn't work in Swift 5.6, for example.

@AnthonyLatsis
Copy link
Collaborator

AnthonyLatsis commented Jun 8, 2022

So would this have to live inside a compiler directive?

Yeah, you would have to branch out on #if swift(>=5.7) to make older compilers happy. Where do these declarations come from?

@freak4pc
Copy link
Contributor

freak4pc commented Jun 8, 2022

There are a few open source projects that use this, we also have some internal stuff that uses it and we won't be moving everyone to Xcode 14 immediately, for obvious reasons :)

Thanks you for the quick response 🙏

@AnthonyLatsis
Copy link
Collaborator

AnthonyLatsis commented Jun 8, 2022

Sorry for the trouble! Apparently a selector is the only place where such a method ref did not cause a compiler crash. Was not expecting this to become a source compat issue, but since compelled selector disambiguation is a fairly narrow case, I believe the consistency in how we treat these refs across different expression contexts is for the best in the log term.

@diyxiaoshitou
Copy link

Sorry for the huge bump but seems like this just became an issue again in Xcode 14. Should I just open a new bug for it?

The workaround no longer works, too.

image

有没有解决方案啊

@lighthouse-codes
Copy link

update to xcode14 and getting this error.. need help...
image

@AnthonyLatsis
Copy link
Collaborator

In Swift 5.7, the type of an unbound reference to an optional method was fixed and changed from ((x) -> (y) -> z)? to
(x) -> ((y) -> z)?. Please read earlier comments for more details.

@AnthonyLatsis AnthonyLatsis added the selectors Feature: Objective-C selectors in Swift (#selector) label Feb 22, 2023
@AnthonyLatsis AnthonyLatsis added optional protocol requirements Feature → protocol: optional protocol requirements swift 3.0 and removed diagnostics QoI Bug: Diagnostics Quality of Implementation labels Feb 22, 2023
@AnthonyLatsis
Copy link
Collaborator

I’m closing this because the disambiguation problem originally reported turned out to be a matter of spelling the types correctly rather than a missing feature. I’ve extracted the error quality bug that was found along the way into a fresh issue: #63834.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler itself optional protocol requirements Feature → protocol: optional protocol requirements selectors Feature: Objective-C selectors in Swift (#selector) swift 3.0 type checker Area → compiler: Semantic analysis
Projects
None yet
Development

No branches or pull requests

8 participants