Why does unwrapping an optional func change its signature?

I'm writing a InputStream (aka NSInputStream) subclass, which has an optional delegate that has one optional function:


public protocol StreamDelegate : NSObjectProtocol {
    optional func stream(_ aStream: Stream, handle eventCode: Stream.Event)
}


When I use the delegate directly, I use the "proper" form of the function, which has a second named label of "handle". But when I unwrap the function, the signature appears to have changed, and Xcode tells me I need to remove the label. [Note: did not try running this code at all.]


guard let delegate = inputStream.delegate, let stream = delegate.stream  else { return }
delegate.stream?(fetcher, handle: .openCompleted)   // OK
stream(fetcher, handle: .openCompleted)             // error: extraneous argument label 'handle:' in call


What's the reason for this (and/or is the compiler wrong)?

Answered by OOPer in 403940022

Optional protocol is not a problem in this case.


But binding a method into a variable causes this issue.

func myFunc(_ a: Int, withLabel b: String) {
    print(b, a)
}

myFunc(1, "label") //Missing argument label 'withLabel:' in call
myFunc(1, withLabel: "label") //valid

let f = myFunc
f(1, "label")   //valid
f(1, withLabel: "label") //Extraneous argument label 'withLabel:' in call

In this example, the type of f is a closure type `(Int, String) -> Void`, and in the current Swift, Closures cannot have labels.

github.com/apple/swift-evolution/blob/master/proposals/0111-remove-arg-label-type-significance.md

What I read in Swift doc:


« Optional Protocol Requirements

You can define optional requirements for protocols, These requirements do not have to be implemented by types that conform to the protocol. Optional requirements are prefixed by the optional modifier as part of the protocol’s definition. »


@objc protocol CounterDataSource {
    @objc optional func increment(forCount count: Int) -> Int
    @objc optional var fixedIncrement: Int { get }
}



Extrait de: Apple Inc. « The Swift Programming Language (Swift 4). » Apple Books.



Did you try it ?

I understand optional protocols. The compiler knows its optional, that's why "delegate.stream" cannot be called directly.


I correctly guessed that I could add a "?" and use this "delegate.stream?(fetcher, handle: .openCompleted)", which compiles just fine.


Then it occurred to me - why not try to get the function as its own variable, assuming it exists, with "let stream = delegate.stream" in the guard statement. That works fine.


But when I go to use this function, I get a warning that the "handle:" parameter should not be there. It is this error I don' understand.

There is something I miss.


When I write your code in iOS or MacOS project

public protocol StreamDelegate : NSObjectProtocol {
    optional func stream(_ aStream: Stream, handle eventCode: Stream.Event)
}

I get the error:

'optional' can only be applied to members of an @objc protocol


Adding @objc before optional,

public protocol StreamDelegate : NSObjectProtocol {
    @objc optional func stream(_ aStream: Stream, handle eventCode: Stream.Event)
}

Still gets the error:

@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes


So, finally, this is OK for compiler:

@objc public protocol StreamDelegate : NSObjectProtocol {
    @objc optional func stream(_ aStream: Stream, handle eventCode: Stream.Event)
}

That's what my reply was about.


Don't you get these compiler errors ?


How is stream declared (used in delegate.stream)

Could you show more complete code ? That will ease investigation.

Accepted Answer

Optional protocol is not a problem in this case.


But binding a method into a variable causes this issue.

func myFunc(_ a: Int, withLabel b: String) {
    print(b, a)
}

myFunc(1, "label") //Missing argument label 'withLabel:' in call
myFunc(1, withLabel: "label") //valid

let f = myFunc
f(1, "label")   //valid
f(1, withLabel: "label") //Extraneous argument label 'withLabel:' in call

In this example, the type of f is a closure type `(Int, String) -> Void`, and in the current Swift, Closures cannot have labels.

github.com/apple/swift-evolution/blob/master/proposals/0111-remove-arg-label-type-significance.md

I learned something !


But the @objc remains needed I assume ?

Why does unwrapping an optional func change its signature?
 
 
Q