Category

Swift: workaround for closure as protocol

Swift Language

In Java, it was common to implement callbacks as anonymous classes. It was nice to be able to define the callback right where you define the button.

Here is a simple JButton ActionListener example:

Wouldn’t it be cool to do the same thing in Swift?

Let’s create a Swift protocol. Just a single method that takes an array of Strings.

Now a method in that uses the protocol.

Straightforward stuff so far.

Now let’s try calling our method using a trailing closure for the delegate.

Blammo. Can’t do it.
How about leaving out the params like this?

Nope. I am disappoint. I wish I could do either of these. So what can we do?

I guess we need a named class the implements the protocol.

Then

Yeah, ok. What if you want to call a method from the calling class?
One way is to pass in the calling class (in this case that is ViewController which has a method named blob()).

In a Java anonymous inner class you have access to the methods and variables of the outer class. Will that work?

Nope. The Swift compiler barfs on its shoes and the XCode editor goes into convulsions.
So, yes, it’s ugly. I’ll be using the NonAnonDelegate:SimpleProtocol version. Swift nested classes don’t have access to the outer class, but you still are able to define them near where they are needed.

Unless you know a better way. Please let me know.

Download the project from this Github repository.

One Reply to “Swift: workaround for closure as protocol”

  1. I found another way. So for example say I have an interface like (note this is Kotlin not Java)
    interface OnConnectionCallback {
    fun onConnected(device:WirelessLezyneDevice)
    fun onConnectionFailed()
    }

    and I use it like
    bluetoothService?.connect(object: OnConnectionCallback {
    override fun onConnectionFailed() { }
    override fun onConnected(device: WirelessLezyneDevice) { } ))

    I want the same sort of thing in Swift so I used a class with member variable closures
    class OnConnectionCallback {
    let onConnected: (WirelessLezyneDevice)->()
    let onConnectionFailed: ()->()
    init(onConnected: @escaping (WirelessLezyneDevice)->(), onConnectionFailed: @escaping ()->()) {
    self.onConnected = onConnected
    self.onConnectionFailed = onConnectionFailed
    }
    }

    and then when I use it the syntax is nice like so

    bluetoothService.connect(scanResult: scanResult, onConnection: OnConnectionCallback(
    onConnected: { (device) in },
    onConnectionFailed: { }))

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.