Custom Threading Operator in Swift

The Swift language supports custom operator functions, similar to operator overloading in C++. This kind of language feature has been known to enable overly “enthusiastic” developers to create a quagmire of unreadable code, which is why I was surprised to see it included in Swift. However, when used with careful consideration and restraint, defining new operators can yield elegant and expressive code with minimal cognitive friction for its readers. In this article I show how to define an operator that simplifies thread marshaling, in particular the transfer of execution from a background thread to the main thread of an iOS app.

The code reviewed in this article is available on Github.
https://github.com/ijoshsmith/swift-threading

This code was compiled and tested against Xcode 6 Beta 5.

Demonstration

The sample project on Github uses a custom operator function in the ViewController.swiftfile. After the user taps a button two anonymous functions execute on a background thread. When a function completes on the background thread an associated function (i.e. closure) executes on the main thread.

When this code executes it logs:

[back] hello
[back] adding...
[main] goodbye
[main] sum = 49995000

Note that the handleButton action method appears to join closures with a mysterious new~> operator…

{ /* background code */ } ~> { /* main thread code */ };

Operator ~>

The Tilde-GreaterThan operator (hereafter known as the marshal operator) is not part of Swift proper. I invented it to express thread marshaling semantics. It executes the lefthand closure on a background thread and when that completes it executes the righthand closure on the main thread. This is a very common thing to do when writing iOS apps that don’t suck, but with Objective-C there was never a terse, streamlined way to express it. Swift to the rescue!

I wrote two versions of the marshal operator. Let’s examine the simple version first.

Since Swift does not contain a ~> operator it is necessary to declare its existence, which is the first line of code seen after importing Foundation. The operator function is declared asinfix because the ~> appears between two closures. This makes sense if you think of the tilde (~) as representing a thread doing its work and then “pointing at” (>) the next closure to execute when it completes.

The next line of code is an implementation detail. That queue constant is bound to a serial GCD dispatch queue on which all background closures execute. I chose to make it serial instead of concurrent because that simplifies the programming model, but feel free to make the queue concurrent if you prefer it that way.

The operator implementation is basic Grand Central Dispatch API usage, simply executing the background closure on a queue and, when it completes, marshaling back to the main thread’s queue to invoke a completion handler.

As seen previously, there is another version of the marshal operator that allows the background closure to return a result, which is passed to the main thread closure as a parameter. That operator function definition is very similar to the one seen above.

Other ideas

This code is just a starting point. There are many similarly useful versions of the marshal operator just waiting to be written. For example, you might have an array of closures on the lefthand side that all must complete before the main thread closure executes, which could make use of a dispatch group to manage completion handling. Or perhaps an operator that chains together multiple background closures and ends with a main thread closure. The possibilities are endless. Just make sure you don’t go overboard with it!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s