Continuation Passing Style in Swift

Asynchronous calls and error handling

Mikael Konradsson
Swift Programming

--

Mike Ash changed my view of how to work with asynchronous calls in Cocoa. How to check for an Error and/or continue execution. Mike Ash is a well known figure in the Cocoa development Community. I highly recommend reading his excellent Friday Q&A — blogs. If you haven’t read them, you should really do so. One blog of his in particular made a deeper impact on my coding style than any other blog I’ve read so far, and the subject of that blog was “Continuation Passing Style”.

But first things first

Apple don’t like the use of Exceptions. Objective-C had support for try / catch / finally but you were recommended not to use it in your production code. Instead as Cocoa Developer you were adviced to use NSError for presenting and handling errors in your applications. In Swift you can’t raise nor catch exceptions (though assert() can be used to force an exception).

So how do you work with NSError effiecent in Swift/Objective-C?

So in both Swift and Objective-C you will probably see code like this:

Ex 1. Objective-C style of handling errors. (Synchronous call)

NSError *error = nil;
NSData *data = [self getSynchronousData:&error];
if (error != nil) {
//An error occurred
return
}
//Continue execeution

Ex 2. Objective-C version translated into Swift (Synchronous call)

var error:NSError?
let data = getSynchronousData(error:&error)
if let error = error {
//An error occurred
return
}
//Continue execution

This can be done much better in Swift because Swift support Tuples.

When something can go wrong in a synchronous call, use Tuples in Swift!

Same example as above, but now with Tuples.

Ex 3. Using tuples (Synchronous call)

func getSynchronousData() -> (data:NSData?, error:NSError?) {
return (NSData(), NSError())
}
let response = getSynchronousData()
if let error = response.error {
//An error occurred
}

What if that function were Asynchronous?

The third example feels alot more natural in Swift, but what if that function were Asynchronous? How would you solve it then? (I can hear you thinking of the delegate pattern, but there is something better).

Let’s create a function that takes two closures as parameters, one for the error, one for data. The idea is simple. If an error occurs, you invoke the error closure otherwise the success/continuation closure.

Ex 4. getAsynchronousData (CPS, Asynchronous call)

func getAsynchronousData(error:(NSError) ->(),
success:(NSData)->()) {
if someErrorOccured {
error(/*Pass the error here*/)
} else {
success(/*Pass the data here*/)
}
}

Ex 5. Call our getAsynchronousData

getAsynchronousData(
{(error:NSError) in println(error)},
{(success:NSData) in println(success)}
)

Simplify with functions

Just like C# we can pass a function with the same parameters as the closure as an argument. So lets create two functions, one for the error and one for processing our data.

func printError(error:NSError){
//handle error
}
func processData(data:NSData) {
//process data
}
//Alt. 1
getAsynchronousData(printError, processData)
//Alt. 2
getAsynchronousData(printError) {
processData($0)
}

So instead of creating a delegate just for receiving data and handle errors, use Continuation Passing Style with closures. It will tighten up your code, the logic is close to the execution, and it will “look” synchronous.

I use this very often, especially when consuming a Web Service.

Follow me on Twitter: https://twitter.com/konrad1977

--

--