Active Filters: Concurrency

We've covered GCD a few times back in Bites #85 & #35. This year, GCD is getting a huge overhaul that brings it into the modern world of Swift. Today we'll take a look at what's changed, let's dive in.

TLDR: Grand Central Dispatch now has a richly typed Swift 3 API.

Here's a basic usage example:

let queue = DispatchQueue(label: "com.jakemarsh.image-processing")

queue.async {
  let thumbnail = image.resize(to: thumbnailSize(for: image))

  DispatchQueue.main.async { imageView.image = thumbnail }
}

Here we create a new queue, and enqueue some image processing work on it. Then, we do the same thing, but back on the main queue, enqueueing the work of setting resized image on our imageView there.

That's just the beginning. Here's a few more examples of how to use the new "swifty" GCD:

Time & Delay:

let delayTime = DispatchTime.now() + .seconds(1)

DispatchQueue.main.after(when: delayTime) {
  print("test")
}

For seasoned GCD users, it may take some getting used to, but the new API is much safer and more intuitive to use in Swift. Neat!

Grand Central Dispatch is the name of Apple's collection of task concurrency functions in libdispatch. GCD (as it's often called) is packed full of interesting features involving concurrency, queuing, timers, and more. Let's dive in and take a look at some simple and common use cases:

Common Usage

One of the most common ways to use GCD is to hop to a background queue to do some work, then hop back to the main queue to update the UI:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
  let resizedAvatarImage = self.resizeImage(avatarImage)

  dispatch_async(dispatch_get_main_queue()) {
    self.avatarImageView.image = resizedAvatarImage
  }
}

Dispatch Once

If we wanted to run some code in viewDidAppear: but only wanted it to run the first time, we could store some flag in a Bool property. Or just use GCD:

struct Tokens {
  static var onceToken: dispatch_once_t = 0;
}

dispatch_once(&Tokens.onceToken) {
  self.initSomeState()
}

Dispatch After

We can also easily wait a specified amount of time, then run some code. Here we'll use this technique to pop back to the previous view controller a quick "beat" after the user selects an an option in a table view controller. (As seen in many apps, including Settings.app):

let delay = 0.5
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
  self.navigationController?.popViewControllerAnimated(true)
}

Topics

#35: Async 🔀

Topics

Grand Central Dispatch is a wonderful way to write asynchronous code in your app. It's actual API however, can sometimes be a bit, well, let's call it "dry". Async is a Swift library by Tobias Due Munk that adds some much-welcomed syntactic sugar to Grand Central Dispatch. Async's core API is simple enough to be understood in a single line of code, so let's look at some other fun things it can do:

Chain Blocks

Async.background {
  // Runs on the background queue
}.main {
  // Runs on the main queue, but only after
  // the previous block returns
}

Delayed Execution

Async.main(after: 0.5) {
  // executed on the main queue after 500 ms
}.background(after: 0.4) {
  // executed on the background queue
  // 400 ms after the first block completes
}

Cancel Blocks

let computeThings = Async.background {
  computeSomethingBig()
}

let computerOtherThings = computeThings.background {
  // this sucker is about to get cancelled
}

Async.main {
  computeThings.cancel() // this won't do anything

  // you can only cancel blocks that haven't yet started executing!

  computerOtherThings.cancel()
  // this second block *would* in fact get cancelled,
  // since it hasn't started executing yet
}

More info about Async can be found at git.io/async