Practical Swift Concurrency: Making the most out of Concurrency in Swift 5.7 and beyond
Section 1: An introduction to asynchronous programming
In the first section of the book we won’t focus on async / await just yet. Instead, we’ll focus on establishing terminology, and an understanding of how asynchronous code works. You will come out of this section with an understanding of what parallelism and concurrency mean. You will also know a bit about why asynchronous code is extremely complex, and you’ll learn how we do asynchronous code without Combine or async / await.
- Async coding
- Callbacks and closures
- Parallelism vs. Concurrency
- GCD basics
Chapter 1: Understanding concurrency in programming
In the first chapter for this book, we’ll establish a baseline understanding of what it is to do concurrency in code. What does it mean to run code concurrently with other code? What does it mean to run code in parallel? And what does asynchronous mean?
This first chapter is not intended to make you master everything related to concurrency. It’s intended to help you establish a baseline that we’ll build on throughout this book.
Chapter 2: Looking at Asynchronous programming in Swift pre-Concurrency
Even though Swift Concurrency deploys all the way back to iOS 13, I think it makes sense to show you the mechanisms that were commonly used in Swift before async/await and other Swift Concurrency features came along.
We’ll take a brief look at Grand Central Dispatch. GCD has been the concurrency utility for iOS developers until Concurrency came along. You won’t learn all the intricate details of GCD; there simply are too many. Instead, we’ll focus on some of the problems GCD had, and why Swift Concurrency will solve these issues.
Section 2: Getting started with Swift Concurrency
Just like any other technology, Swift Concurrency can be explained using a quick hello world example. This section is Swift Concurrency’s “hello world”. You will start your Swift Concurrency journey by learning a bit about the basic syntax of async / await. This is by far the most common way to interface with async code and it’s important that you learn how to call async code. This also requires you to learn about tasks.
Once you’ve seen how you can call existing asynchronous functions, you will learn how to write your own asynchronous functions.
There will be a lot of focus on what happens when a new Task is created. This means that you will walk out of this section with a good understanding of suspensions, resuming, and cancellation.
You will also learn how you bridge your existing callback code into the async/await world, and how Xcode can help with that.
- Async / await
- Suspension and the heap
- Tasks basics
- Calling and writing async functions
- Bridging existing code into Swift concurrency
Chapter 3: Awaiting your first async methods
This chapter’s purpose is to introduce you to Swift Concurrency gradually. We’ll take a look at the basic syntax of async/await, throwing async, and non-throwing async. You will learn about async and sync contexts, and how you can get yourself into an async context.
We’ll also dig in and take a brief look at what happens when you await something; this ties back into chapter one. You will also learn how you can write your own async functions, and how these functions don’t have to suspend.
By the end of this chapter you will be able to start writing your very first async code, and you will have a sense of confidence when doing this!
Chapter 4: Understanding Swift Concurrency’s tasks
Tasks are the essential building pieces for concurrency in Swift. In this chapter, you will learn everything you need to know about tasks, and how they relate to each other. By the end of this chapter, you should have a good sense of how your code works when they spawn tasks, and you will understand that:
- We don’t talk about threads in Swift Concurrency
- Task is the new thread
- We will never have more threads than CPU cores to avoid thread explosion
Chapter 5: Existing code and Swift Concurrency
This chapter acknowledges that developers won’t always work on a fresh codebase. We’ll take a look at methods to bridge existing callback based code into the world of async / await. You will learn about continuations and how they are used to convert “regular” code to async await code.
You will also take a first look at bridging Combine code into async await by taking code that obtains a single value (like network calls) and awaiting the first emitted value. In the next chapters you will get a proper introduction into AsyncSequence to iterate over multiple emitted values.
Section 3: Understanding actors and data isolation in Swift Concurrency
Section 3 is all about actors, actor isolation, and safely passing stuff around. You will learn how and when you should be building custom actors, and when it might not make sense to build a custom actor. In addition to actors, you will also learn a lot more about the Sendable protocol, and about how objects, and values can and cannot be passed around depending on their sendability.
- Reusing tasks / sharing a tasks
Chapter 6: Preventing data races with Swift Concurrency
A huge feature of Swift Concurrency is the existence of actors. In this chapter we’ll explore actors in depth. You will learn how actors ensure that access to their state is free of data races.
We’ll also look at global actors, and in particular the main actor. You will learn how the global main actor works, and why it’s an important part of the Swift Concurrency system.
We will also explore the Sendable protocol as well as @Sendable closures. You will learn what sendability is, and why it’s important. This chapter also explores how Swift Concurrency is dedicated to making sure that our code is thread safe on a compiler level.
Section 4: Advanced uses cases for Swift Concurrency
In section 3 you’ve established a good understanding of how you can make use of async / await and Swift Concurrency for a single task. In other words, you will know how you can call an asynchronous function. You’re also able to write a function that can be called asynchronously. In this section, the focus will be on more complex tasks like running multiple tasks in parallel, and consuming async sequences.
By the end of this section, you will understand how you can write code in a way that allows you to perform a batch of actions that can run in parallel. You will know how to await the outcome of these tasks, and how they are expected to participate in Swift’s cooperative cancellation.
- Async sequences
- Async streams
- Task groups
- Structured concurrency
Chapter 7: Working with asynchronous streams
In this chapter, you will learn about Swift Concurrency’s
AsyncSequence protocol and how we can use to iterate over a collection that produces or obtains its data in an asynchronous fashion. We will start by exploring a built-in mechanism that allows us to receive and process contents of a URL line by line as soon as each line is fetched from the network.
After that, we’ll look at using an
AsyncStream to build your own stream of values by building a wrapper for a websocket connection.
By the end of this chapter you will have a solid understanding of how Swift Concurrency allows us to iterate over an asynchronous collection, and how you can leverage built-in mechanisms to build your own asynchronous sequences of values.
Chapter 8: Async algorithms and Combine
In Chapter 7, we took a look at async sequences and how they can be used in Swift. There are lots of parallels between Apple’s Combine framework and the things we can do with Swift Concurrency. In this chapter, you will learn more about the similarities and differences between async sequences and Combine.
We will also take a look at the async algorithms package that Apple has released as a Swift package that we can optionally use in our projects. This package provides certain functionality that we have in Combine but that’s not available for async sequences out of the box. With this package we can come a lot closer to what Combine does.
Chapter 9: Performing and awaiting work in parallel
Awaiting asynchronous work is great because it means that we can do things like making a network call while the user is scrolling through a list of content. In reality the true power of concurrency isn’t to perform one task asynchronously, but to perform many tasks in parallel.
In this chapter you will learn about async let and task group, and how these two tools allow you to spawn tasks that all run in parallel as child tasks of another task. You will see how we can leverage these tools to build a highly concurrent data fetcher that makes hundreds of network calls in parallel.
We will also use the information in this chapter as a bridge into fully understanding structured concurrency and its implications.
Section 5: Concurrency best practices
This last section in the book is all about wrapping up what you’ve learned. You will learn how you can make sure that your code works well by writing tests for it, and you will learn how you can use Instruments to gain insights into how your app is running your tasks.
- Writing tests for concurrent code
- Profiling concurrency with Instruments
Chapter 10: Swift Concurrency and your unit tests
In this chapter, you will take a look at XCTest and to what extent it supports testing asynchronous code. You will learn how you can write asynchronous tests that await your async functions as well as tests for code that uses async sequences.
By the end of this chapter you will know exactly how you can test your async code, and where it makes sense to fall back to more old school testing methods.
Chapter 11: Debugging and profiling your asynchronous code
The final chapter of this book focusses entirely on Instruments. Knowing how you can leverage Instruments to profile and debug your code is essential to maintaining good performance, and to improving performance when things go wrong.
We’ll start this chapter by inspecting how Instruments visualizes our tasks. You will learn more about the different states tasks can be in, and you will learn how you can see the structured concurrency relationships between certain tasks in your code.
After that you will learn how you can gain insights into your how your actors are scheduling work, and how that scheduling impacts your tasks. We will work on a sample app that has a few glaring performance issues and you’ll see how we can fix this issues while constantly verifying our work through Instruments.
You'll get the book in epub and PDF format and a zip file with sample projects.