Understanding Structured Concurrency in Swift

It’s been a few years since I’ve heavily been in the Swift world. A lot has changed from the days of MVC-heavy architecture, UIKit and good old Interface Builder, and the occasional foray into newer, more modern patterns like MVVM and FRP with RxSwift. While I’ve enjoyed spending time with React and better understanding the wide world of JavaScript, I’ve been transitioning back to native iOS and Swift recently. In the process, one of the biggest standout themes is structured concurrency.

This post is a little playground for myself to try and better understand the evolution of concurrency and design patterns around data flow in Swift. Starting with a brief exploration into the background, historically there are a number of patterns for dealing with data, including:

  • Delegation

  • Publisher-subscriber

  • Callbacks

  • FRP with third-party frameworks like RxSwift

  • Grand Central Dispatch

Issues with completionHandlers:

  • guard let {}, else return all the things!

  • No way to enforce that closures are always invoked, leading to bugs

  • Extra noise when working with closures

Now there are ways to improve asynchronous code, including:

  • Result type to enforce conformity

  • Futures

  • Better but still not great

Now this is certainly workable, and if you’ve been working in Swift, and really in any software development position over the last 5 years you’ve surely used many of the above concepts, but is there no better way? There is! Introducing structured concurrency as a first class citizen in Swift! Enter async/await.

  • Allows for async to safely unblock thread

  • Allows for try-throw pattern to eliminate a lot of boilerplate with error handling

  • Results in fewer bugs relating to completion handlers

Other thoughts and callouts

What is an AsyncSequence? Just like a normal Sequence, except that it vends its elements asynchronously. It allows for await in for loops.

To be continued…