# async/await and Structured Concurrency — Swift

Source: https://www.skillbyai.com/en/swift/c-async

> Write asynchronous code with async/await, async let and task groups.

## Asynchronous code that reads like synchronous code

Swift's concurrency model (Swift 5.5+) replaces nested completion handlers with **`async`/`await`**. An `async` function can **suspend** at each `await`, freeing its thread to do other work, and resume when the awaited operation completes; the code still reads top to bottom. Errors work naturally with `async throws` and `try await`. **Structured concurrency** runs work in parallel with clear lifetimes: **`async let`** starts child tasks that run concurrently and are awaited later, and **`withTaskGroup`** or `withThrowingTaskGroup` handle a dynamic number of child tasks. Child tasks cannot outlive their parent scope, and cancellation and errors propagate automatically. **`Task { }`** starts new top-level asynchronous work from synchronous code (for example from a button action), inheriting the current actor context, while `Task.detached` does not. Apple's frameworks offer async APIs such as `URLSession.data(from:)`. Avoid blocking threads inside async code with semaphores or sleeps; use `try await Task.sleep(for: .seconds(1))` when you need a delay.

## Parallel child tasks

A parent task starts several child tasks, awaits them all, and they cannot outlive it.

![A long horizontal bar for the parent with three shorter parallel bars branching below it and rejoining before the parent ends.](assets/figures/swift/section-7-map.svg) — Figure 7.1 — Structured concurrency with child tasks.

## Fetching several resources concurrently

async let runs requests in parallel; the function awaits them together.

```swift
import Foundation

struct Product: Decodable { let id: String; let title: String }
struct Review: Decodable { let rating: Int; let text: String }

func fetch<T: Decodable>(_ type: T.Type, from url: URL) async throws -> T {
    let (data, response) = try await URLSession.shared.data(from: url)
    guard (response as? HTTPURLResponse)?.statusCode == 200 else { throw URLError(.badServerResponse) }
    return try JSONDecoder().decode(T.self, from: data)
}

func loadProductPage(id: String) async throws -> (Product, [Review]) {
    let base = URL(string: "https://api.example.com")!
    async let product = fetch(Product.self, from: base.appending(path: "products/\(id)"))
    async let reviews = fetch([Review].self, from: base.appending(path: "products/\(id)/reviews"))
    return try await (product, reviews)          // both requests run concurrently
}

func loadPrices(for ids: [String]) async throws -> [String: Int] {
    try await withThrowingTaskGroup(of: (String, Int).self) { group in
        for id in ids {
            group.addTask { (id, try await fetchPrice(id)) }
        }
        var prices: [String: Int] = [:]
        for try await (id, price) in group { prices[id] = price }
        return prices
    }
}
```

## await is a suspension point

Anything can change while a function is suspended at `await`: other code may run and modify shared state. Re-check assumptions after an await rather than relying on values read before it.

**Quiz:** What does `async let` do?

- [x] Starts a child task that runs concurrently and is awaited later
- [ ] Runs code synchronously
- [ ] Creates a detached task with no parent
- [ ] Blocks the thread until done

*Answer:* Starts a child task that runs concurrently and is awaited later. async let creates a concurrent child task bound to the current scope.
