TL;DR
A concise guide explains Swift's concurrency model: async/await for suspension, Tasks and TaskGroups for managing concurrent work, and actors/MainActor for compile-time isolation. New Xcode defaults aim to make concurrency less surprising while offering opt-ins for CPU-bound work.
What happened
The source breaks down Swift’s concurrency features to make them easier to reason about. It presents async/await as a way to write code that suspends and resumes without callback nesting, and shows how async let enables parallel work. Tasks are introduced as runnable units that bridge synchronous and asynchronous code, with TaskGroups enabling structured, parallel subtasks whose cancellation and error behavior is propagated through the group. The guide shifts focus from threads to data isolation: MainActor, actors, and nonisolated members define who may access state, and the compiler enforces those boundaries to reduce data races. It also explains that Swift Concurrency builds on libdispatch for scheduling, and highlights two Xcode build options — defaulting everything to MainActor and making nonisolated async functions stay on the caller’s actor — plus the @concurrent annotation to opt CPU-heavy tasks off the main actor.
Why it matters
- Helps developers write clearer asynchronous code that suspends rather than using nested callbacks or complex reactive pipelines.
- Compile-time isolation (actors/MainActor) reduces the risk of data races that are hard to reproduce at runtime.
- Structured Tasks and TaskGroups make managing cancellation and error propagation more predictable.
- Xcode defaults lower the friction for adopting concurrency while allowing explicit opt-ins for background work.
Key facts
- Async functions are marked async and must be awaited from other async code; await suspends execution until completion.
- async let starts concurrent child work immediately; awaiting the variables collects their results.
- Task is the runtime unit that runs async work and can be created from synchronous contexts to start async operations.
- withThrowingTaskGroup organizes parallel subtasks; cancellation of a parent cancels children and thrown errors cancel siblings.
- Actors provide an isolation boundary guaranteeing exclusive access to their mutable state; external calls require await.
- @MainActor marks data or types as belonging to the main actor’s isolation domain, which the compiler enforces.
- nonisolated members can be called from any context without await but must not access actor-protected state.
- Swift Concurrency uses libdispatch under the hood; the compiler adds the isolation layer while the runtime schedules work.
- Two Xcode build settings simplify the model: default actor isolation set to MainActor and approachable concurrency that keeps nonisolated async on the caller’s actor.
- @concurrent is an opt-in way to run CPU-intensive async functions off the main actor.
What to watch next
- How the Xcode 26 defaults (MainActor isolation and approachable concurrency) change behavior in existing codebases as projects migrate.
- Performance impacts when moving CPU-bound work off MainActor using @concurrent — measure before optimizing.
- The potential for subtle data races in high-concurrency environments if isolation boundaries are misunderstood.
Quick glossary
- async/await: Language primitives that let functions pause (suspend) and resume, enabling sequential-looking code for asynchronous operations.
- Task: A runtime unit that runs asynchronous work; tasks can be started from synchronous code and can be awaited, canceled, or left running.
- Actor: An isolation construct that protects its mutable state by ensuring only one piece of code accesses it at a time.
- MainActor: A global actor representing the main isolation domain commonly used for UI-related state that the compiler enforces.
- TaskGroup: A structured concurrency primitive for launching and coordinating multiple child tasks, with defined cancellation and error semantics.
Reader FAQ
Can I use await outside an async function?
No — await must be used inside an async context.
Do actors run on their own threads?
No — actors are isolation boundaries; the runtime chooses which thread executes actor code.
Does @MainActor force dispatch to the main thread?
It declares that the value or function belongs to the main actor’s isolation domain; the compiler enforces crossing that boundary rather than directly describing a dispatch mechanism.
Are the approachable concurrency defaults enabled in new projects?
Yes — the source says new Xcode 26 projects enable MainActor as the default actor isolation and set approachable concurrency to YES.
Is there an automatic background-offload for nonisolated async functions?
Not confirmed in the source
Fucking Approachable Swift Concurrency Finally understand async/await, Tasks, and why the compiler keeps yelling at you. Huge thanks to Matt Massicotte for making Swift concurrency understandable. Put together by Pedro…
Sources
- Approachable Swift Concurrency
- Embracing Swift concurrency – WWDC25 – Videos
- Mastering Swift Concurrency: A Practical Guide
- Approachable Concurrency in Swift 6.2: A Clear Guide
Related posts
- How the British Empire Built a Resilient Global Subsea Telegraph Ring
- curl eliminates strcpy usage, extending its earlier strncpy ban
- Times New American: State Department’s Typeface Swap and Its Meaning