Skip to content
Prepare to Grow

Swift Fundamentals

Async/Await and Swift Concurrency

Write structured asynchronous code with async/await, tasks, actors, cancellation, and Sendable values.

Beginner5 min read2 guided activities

Before you begin

What you will understand

  • Read and write async functions.
  • Use structured tasks and cancellation.
  • Protect shared mutable state with actors.
  • Recognize MainActor and Sendable boundaries.
01

Learning stage

Concept Explanation

An async function can suspend without blocking its underlying thread. Call it with await from another asynchronous context. async describes suspension; throws describes failure, so a function can use both. Structured concurrency keeps child work connected to a parent task for lifetime, cancellation, and priority.

Concurrency also introduces isolation. Actors serialize access to their mutable state, @MainActor protects UI-facing state, and Sendable describes values that can safely cross concurrency boundaries. Cancellation is cooperative: check for it during long work and allow cancellation errors to propagate when appropriate.

  • async/await: declare and call potentially suspending work.
  • Task: start asynchronous work in the current context.
  • actor: isolate mutable state from concurrent access.
  • @MainActor: isolate UI-related work to the main actor.
  • Sendable: describe values safe to transfer between concurrency domains.
02

Learning stage

Syntax & Examples

The service performs two independent requests concurrently and updates UI state on the main actor.

ProfileViewModel.swiftswift
struct Profile: Decodable, Sendable { let name: String }
struct Activity: Decodable, Sendable { let count: Int }

@MainActor
final class ProfileViewModel {
  private(set) var summary = "Loading"

  func load() async throws {
    async let profile = fetchProfile()
    async let activity = fetchActivity()
    let result = try await (profile, activity)
    summary = "(result.0.name): (result.1.count) activities"
  }
}
async let creates structured child tasks. The method resumes on the main actor before changing UI-facing state.
03

Learning stage

Real-World Usage

These concepts appear throughout production iOS code. Focus on the requirement each technique communicates.

  • Load network data without blocking the interface.
  • Run independent requests concurrently with async let or a task group.
  • Use actors for caches, repositories, or shared coordination state.
  • Cancel obsolete work when a screen disappears or a new request replaces it.
04

Learning stage

Common Mistakes

Notice these mistakes early so the compiler and type system can help instead of becoming obstacles.

  • Using Task to hide an unclear lifetime instead of making an API async.
  • Starting unstructured work when child tasks should be scoped to a parent.
  • Ignoring cancellation during long or repeated operations.
  • Moving non-Sendable mutable state across isolation boundaries.
  • Performing CPU-heavy synchronous work on the main actor.
05

Learning stage

Practice Exercises

Work through these activities in a small Xcode project. No embedded playground is required on this site.

Activity 1

Create an async loading API

Write an async throws function that loads a profile and returns it. Call it from a @MainActor view model and represent loading, success, and failure.

Hint: Keep the network function independent of UI state.

Activity 2

Protect shared state with an actor

Create an actor that records completed download IDs and exposes a count. Call it from two tasks.

Hint: Access to actor-isolated state usually requires await.

06

Learning stage

Knowledge Check

Answer each question before revealing the explanation.

QUESTION 1

What does await indicate?

  1. A.The function may suspend at this point
  2. B.A new thread is always created
  3. C.The operation cannot fail
  4. D.The value becomes optional
Reveal answer

The function may suspend at this point

Suspension lets other work proceed without blocking the underlying thread.

QUESTION 2

What problem does an actor primarily address?

Reveal answer

It isolates mutable state so concurrent code cannot access it unsafely.

Actor isolation makes access cross an explicit concurrency boundary.

07

Learning stage

Interview Preparation

Explain the idea in terms of its purpose, behavior, and tradeoffs rather than reciting syntax.

Why prefer structured concurrency for related child work?

Short answer: It ties child task lifetime, cancellation, priority, and results to the parent scope.

Explore the detailed answer

Structured child tasks cannot silently outlive the operation that created them. The parent waits for their results, cancellation can flow through the hierarchy, and errors remain part of a visible control flow. Unstructured Task still has uses, but its ownership should be explicit.

Possible follow-ups

  • • What does @MainActor guarantee?
  • • How do actors differ from serial queues?
08

Learning stage

Key Takeaways

Swift concurrency makes suspension and isolation visible in the type system. Prefer structured task lifetimes, protect mutable state with actors, respect Sendable boundaries, and treat cancellation as part of the operation's contract.

  • await marks a possible suspension point.
  • Structured tasks inherit a parent lifetime.
  • Actors protect isolated mutable state.
  • @MainActor and Sendable make concurrency boundaries explicit.
  • Cancellation requires cooperation.