Learn to coordinate concurrent work, protect mutable state, and give every task a clear lifetime. The topics move from suspension and structured tasks to isolation, streams, and diagnosing failures. Each article builds a working example, measures the behavior it describes, and ends with an exercise.
The examples use Swift 6 language mode and were compiled and run with the Swift 6.4 toolchain from Xcode 27.1 on macOS 26. Features from Swift 6.2 to 6.4, such as @concurrent, Task.immediate, cancellation shields, and the noncopyable Continuation, are labeled where they appear, together with the OS versions they require.
Read the examples in order within each article: later Swift blocks extend the earlier definitions and include executable assertions. Diagrams show task trees, interleavings, and state machines; equations appear where queueing, scheduling, or probability gives a precise answer, such as how many requests to keep in flight or how often responses arrive out of order. The earlier Swift Concurrency: async/await, Tasks, and Actors gives a one-article overview of the whole model.
Article available Coming soon
1. Foundations
Concurrency vs. Parallelism in Swift: Understanding the Difference
Concurrency and parallelism are often used as synonyms, and the confusion is expensive. Teams add tasks to make a CPU-bound loop faster...
Threads, Dispatch Queues, and Swift Tasks: Three Layers of Concurrent Work
A Swift task is not a thread, and a dispatch queue is not a thread either. Each is a different layer of...
Suspending Functions with async and await in Swift
An async function is a function that may give up its thread partway through and continue later. await marks each place where...
Running Independent Work with async let in Swift
Consecutive await statements run one after another. That default is safe, but when a screen needs three independent pieces of data, it...
Structured Concurrency and Child-Task Lifetimes in Swift
Structured programming made control flow follow the shape of the source code: a block is entered at the top and left at...
2. Task Ownership
Creating Tasks and Choosing Their Lifetime Owner in Swift
Task { } is the most common way to start asynchronous work from synchronous code, and the easiest one to misuse. It...
Task Groups in Swift: Fan-Out, Aggregation, and Early Exit
async let handles a fixed number of concurrent operations known when you write the code. A task group handles everything else: a...
Cooperative Cancellation and Cancellation Checks in Swift
Cancelling a Swift task does not stop it. It sets a flag. The task keeps running exactly as before until some code...
Cancellation Handlers and Reliable Resource Cleanup in Swift
Cooperative cancellation works when the cancelled code is running and can check a flag. It does nothing for code that is suspended,...
Unstructured and Detached Tasks in Swift: When to Use Each
Swift has two ways to start a task that is not a child of the current one. Task { } creates an...
Task Priority, Inheritance, and Priority Inversion in Swift
A task’s priority tells the scheduler how urgent its work is. It does not make the work faster, it does not interrupt...
3. Isolation and Shared State
Actors in Swift: Protecting State Behind an Isolation Boundary
Mutable state shared between concurrent tasks needs protection. For decades that meant locks, queues, and discipline: every access had to remember to...
Actor Reentrancy in Swift: Keeping Invariants Across Suspension Points
An actor guarantees that only one piece of its code runs at a time. It does not guarantee that a method runs...
The Main Actor and UI-Facing State in Swift
UIKit, AppKit, and SwiftUI share one rule older than Swift itself: user interface state is touched only from the main thread. For...
Sendable in Swift: Moving Values Across Isolation Boundaries
Every data race needs two things: a piece of mutable memory, and two concurrent contexts that can both reach it. Actors and...
Isolated and Nonisolated Methods in Swift
Every function in Swift 6 has an isolation: a static answer to the question “which actor’s state may this code touch synchronously?”...
Choosing an Executor in Swift Without Blocking the UI
Isolation decides which state code may touch. An executor decides where that code actually runs: on the main thread, on a thread...
Migrating to Swift 6 Data-Race Safety
The Swift 6 language mode turns data-race safety from advice into a compile-time guarantee: a module that builds in Swift 6 mode...
4. Streams and Legacy APIs
Consuming Values with AsyncSequence in Swift
An async function produces one value, later. An AsyncSequence produces many values over time: lines of a file as they are read,...
Building Streams with AsyncStream and AsyncThrowingStream in Swift
Most event sources in an app were built before Swift concurrency: delegates that report location fixes, callbacks that fire for every download...
Buffering, Backpressure, and Stream Termination in Swift
Whenever a producer can generate values faster than a consumer can handle them, something has to give. The values can pile up...
Bridging Callback APIs with Checked Continuations in Swift
A continuation is the bridge between the callback world and the async world. Inside withCheckedContinuation, the current task suspends and hands you...
Continuation Ownership, Cancellation, and Double Resume in Swift
A continuation wrapper is easy to write when exactly one thing can finish the operation. It becomes hard as soon as two...
5. Testing and Diagnosis
Rejecting Stale Results After Request Replacement in Swift
A user types “swift” into a search field. Five requests go out, one per keystroke. The response for “s” is large and...
Testing Concurrent Swift Code with Controlled Interleavings
A concurrent test that sleeps is a test that guesses. It guesses how long the work takes on the machine it happens...
Avoiding Deadlocks and Blocking Waits in Swift Concurrency
Swift concurrency removes the most common way to deadlock: actors are reentrant, so two actors that call each other do not lock...
Finding Data Races in Swift with Thread Sanitizer
Swift 6 proves the absence of data races at compile time, for code it can check. Every real codebase also contains code...
Profiling Task Lifetimes and Main-Actor Contention in Swift
Concurrency problems that survive code review and testing usually show up as time: a screen that takes 300 ms longer to appear...
Up next
Networking