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.swift file. 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 as infix 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!
I can understand this code, it seems pretty easy, but this line surprised me
mainClosure(result: result)
I was expecting
mainClosure(result: R)
Why do you have result: result ?
When executing a closure, a parameter is preceded by its name with a colon. This is different from declaring a closure, in which case you provide the parameter name with a colon followed by its type. So, mainClosure(result: result) is just executing the closure where the result: parameter is passed the `result` value.
Ah, yes. Silly me. I’m still experiencing a bit of indigestion with the Swift syntax.
Pingback: Dew Drop – July 7, 2014 (#1809) | Morning Dew
This is damn clever, but why are you forcing the result to be optional? This code doesn’t have any error conditions of its own to report with a nil, and the operator’s user can always use an optional type if she needs it for her particular use case.
(P.S. It might make sense to write a variant that accepts a queue/closure tuple on the left side, or on either side, to make this work with any queue you need to use.)
Excellent points! I hadn’t considered that the generic type argument could itself be optional. I’ll make the change soon. I’m intrigued by your P.S. suggestion, though for now I’d prefer to keep this simple and use a dedicated queue. Perhaps if I find myself needing a custom queue option later I’ll follow your advice, Brent.
Here’s a fun little brain teaser involving the marshal operator.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
marshalMadness.swift
hosted with ❤ by GitHub
Your ending paragraph about chaining is interesting because it almost sounds like you could implement a Promise API for async programming using your marshal operator.
Hey Dave. That would be an interesting project. I saw a futures implementation in Swift that might be a good starting point. http://brockerhoff.net/blog/2014/06/26/swift-a-simple-future/
Pingback: Custom Threading Operator in Swift | VishalDharmawat - iPhone and iPad Development
Hey Josh, this is a great example for using custom operators. However, could you clarify the need to include semi-colons after each call to the marshal operator?
Thanks slakor. I wish I could! When I first wrote this code in Xcode 6 Beta 2 it compiled just fine without semicolons. Ever since Beta 3 the compiler gets confused without them. I can’t explain why.
If you declare your operator with @assignment each use of it will be a complete statement, so no need for semicolons; in other words, going from:
a+b; c+d;
to:
a+=b
c+=d
However, in that case, <~ would perhaps look better 😉
Pingback: A Swift App that Calls JSON Web Services | iJoshSmith
Hi Josh, why not use a UNIX shell-like syntax i.e. & to build dispatch groups and | for the final marshal to the main thread https://github.com/johnno1962/SwiftRegex
I see no reason why not, John. It boils down to personal preference and inclination.