Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to use Void type in a closure of protocol with associatedtype in Swift

I've a little issue I cannot find an elegant way to use a closure with an associated type as a Void in a protocol.

Let's say I've the following protocol:

protocol MyProtocol {

  associatedtype OutputDataType
  associatedtype InputDataType

  var isCompleted: ( (InputDataType) -> OutputDataType)? { get set }
}

Now I've a class that conforms to this protocol with Void type inside the closure like this:

class MyClass: MyProtocol {
  var isCompleted: ( () -> Void )? // Error: Type 'MyClass' does not conform to protocol 'MyProtocol'
}

So I tried this:

var isCompleted: ( (Void) -> Void )? // warning: When calling this function in Swift 4 or later, you must pass a '()' tuple; did you mean for the input type to be '()'?

And ended up with this "weird" syntax:

var isCompleted: ( (()) -> Void )?

or this verbose one:

typealias InputDataType = Void
var isCompleted: ( (InputDataType) -> Void )?

But now when I want to assign this closure in an other class I need to explicitely use a "_" in parameter:

myClass.isCompleted = { _ in
// other code
}

Isn't possible to use something like this:

myClass.isCompleted = {
// other code
}

So do you know if it would be possible to get something more elegant like the example I quoted and expected?

like image 639
DEADBEEF Avatar asked Jan 24 '26 11:01

DEADBEEF


1 Answers

Because you've declared isCompleted as necessary via the protocol, you will have to implement it in MyClass and unfortunately use the "weird" syntax.

However, to make using it nicer, you can add an extension to the protocol for when InputDataType == Void that gives a convenience to accessing isCompleted without having to explicitly pass Void... Something like this:

extension MyProtocol where InputDataType == Void {
    var isCompleted: (() -> OutputDataType)? {
        get {
            guard let isCompleted = self.isCompleted else { return nil }
            return { return isCompleted(()) }
        }
        set {
            if let newValue = newValue {
                self.isCompleted = { _ in return newValue() }
            } else {
                self.isCompleted = nil
            }
        }
    }
}

Then you can use the method on your class through the convenience, which gives you the last code example you were looking for:

var myClass = MyClass()
myClass.isCompleted = {
    print("Hello World!")
}
myClass.isCompleted?()
like image 200
theRealRobG Avatar answered Jan 26 '26 02:01

theRealRobG