Practical Swift Concurrency
Make the most out of Concurrency in Swift 6.0 and beyond
Get Practical Swift ConcurrencyAbout the book
Practical Swift Concurrency is a book written by Donny Wals, aimed at intermediate to advanced developers who want to learn more about leveraging Swift Concurrency in their apps. The book will take you on a journey through Concurrency in Swift using Donny's distinct practical approach. We'll focus on the important bits and pieces of Swift Concurrency that will help you build a solid foundation for using Concurrency as soon as possible.
Your journey into Swift Concurrency will start with a general overview of the available concurrency constructs in Swift. After that, you will await your first methods calls and work your way up to building a complex and highly concurrenct system. We also go over important skills like testing async code as well as profiling and debugging your code through Instruments.
In other words, Practical Swift Concurrency helps you build a strong foundation and mental model around all of the essential ideas, concepts and principles to get you up and running with the concurrency in Swift without making anything more complex than it needs to be.
Chapter overview
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.
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 essentially 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.
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.
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.
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
In this chapter we focus 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.
Chapter 12: Migrating to Swift 6
Now that you've learned a ton about Swift Concurrency, how it works, what it does, and how it's used, it's time to take a look at Swift 6.
In this chapter our main focus will be to understand what the Swift 6 rollout looks like for Concurrency. We will take a look at how Xcode 16 handles Swift concurrency and Swift 6 in terms of new projects and how Swift 6 works in Swift Package Manager as well. You will also learn about the ways in which you can make your Swift 6 code cooperate with non-Swift 6 code and we will look at different migration strategies that you might apply to when you want to enable the Swift 6 language mode in your projects.
Other books by Donny Wals
Practical Core Data
Practical Core Data is for intermediate to advanced developers who want to learn more about Core Data. Whether you're new to Core Data, or tried using it years ago, you'll find that Practical Core Data introduces you to all the essentials to get you up and running with the framework.
Learn morePractical Combine
Practical Combine is a book aimed at intermediate to advanced developers who want to learn more about Apple's Combine framework. This book takes you all the way from the basics to building custom Combine publishers using Practical, useful examples that you can start using immediately.
Learn more