Async.legacy - Swift wrapper for Grand Central Dispatch (iOS7 and OS X 10.9 compatible)

While GCD is a fairly simple API enabling blocks to be dispatched asynchronously on different threads or on particular queues that doesn't mean that the API can't be cleaned up a little to make it really nice to use with Swift.

Tobias Due Monk released the Async library that does just that. I saw the announcement probably on iOSDevWeekly and I liked what I saw except there was one fatal issue for me in that the library required iOS 8 or OS X 10.10 and I haven't updated my devices yet let alone moved my target up to that version yet. I actually just left it and ignored it but having done the optimisation of the GrayScott code presented at the last Swift London meetup I felt comfortable enough with GCD that I could implement most of the fuctionality to work with iOS 7 (and OS X 10.9 Mavericks but just take that as read every time I mention iOS 7 in this article). The result was Async.Legacy

How to Use Async (or Async.legacy)

All you need to do is to drop in the Async.swift (or AsyncLegacy.swift if you are using iOS7) into your project and then you can call the methods. There is full API documentation in the Readme files and that are easily viewable on the Github pages but essentially what Async allows you to do is chain a series of Asyncronous calls rather than to nest them. The result looks like:

Instead of having to nest the calls like this:

While in this simple example it doesn't matter too much if you need to chain a large sequence of asynchronous calls on it could be made a lot cleaner. There are also other functions available allowing the calling code to wait on the completion of a block.

How Does the Syntax Work

While it may appear at first glance that there is some operator overloading going on in fact all that happens is the static methods of the Async struct (actually a class in Async.legacy) returns an instance of Async that has methods of the same names as the classmethods. If you call the methods on the instance it will execute them on the completion of that block. The way that this works is made more obvious if you capture the variable for the block:


Once you see that the apparent magic is gone but you can still use the nice clean syntax and code layout shown in the original example.

How do the Libraries Work

Async

This uses the new iOS 8 and 10.10 Yosemite API calls allowing you to wait or notify on particular blocks. The returned struct contains the block you are calling and the API is a fairly direct translation.

Async.legacy

Without most of the API for operating directly on dispatch_block_t's the Async.legacy creates a dispatch group for each block and wraps that in the returned object. It then uses the dispatch_group_notify and dispatch_group wait to control launch the chained blocks (wrapping each of them in new object and returning it).

For the delayed dispatch uses dispatch_after and manages the group by manually calling dispatch_group_enter and then calling dispatch_group_leave from a block that is created to wrap around the main block passed in.

The approach to make the blocks cancellable is similar. Every block is wrapped in an outer block that tests if it has been cancelled before running it. The cancelled state is stored in the object representing the block and the need to have this shared mutable state is the reason Async.legacy cannot return a struct but must return the object.

Which Version Should I Use

If you a building for iOS 8 then you should definitely use Async. It directly calls provided API without having to do things like wrapping blocks in other blocks or to maintain attached objects with each block for it's lifetime. It also allows you to fully take advantage of the clever new quality of service classes. See the GCD WWDC talk for details.

However Async.legacy is a complete drop in replacement with everything except the QOS classes working (raise a Github issue if you find anything that doesn't work) and you can use it while you continue to support iOS7 and then when you do drop iOS7 as a target for other reasons you can switch to the main Async project simply by removing the AsyncLegacy.swift file from the project and adding Async.swift.

Please give it a try and let me know what you think!

I'll be at iOSDevUK next week if anyone wants to talk to me about this or anything else then.