What does async/await solve? async/await
Linear, structured async code — no callback pyramids.
Mark an async function async/await
func fetchUser() async throws -> User { ... }Call an async function async/await
let user = try await fetchUser()
What is a Task? async/await
A Task in Swift Concurrency allows you to call async methods inside a synchronous context. Take the following two methods:
func someSynchronousMethod() {
Task {
await someAsynchronousMethod()
}
}Why would I want to keep a reference of a task?
A task runs regardless of whether you track it. However, without a reference, you can’t wait for its results or cancel its execution.
Task cancellation basics async/await
Cooperative: check
Task.isCancelled // This can be useful if you don’t want the task to fail but return a default value instead.
or
try Task.checkCancellation(). // Throw and error if the task was already cancelled
check if task was cancelled async / await
let task = Task {
print("This is first")
let sum = (1...100000).reduce(0, +)
try Task.checkCancellation()
print("This is second")
}
print("This is last")
task.cancel()
Output:
~~~
This is first
This is last
~~~
What happens if a user cancels a task that have child tasks?
If a parent task is canceled, all of its child tasks are also automatically notified about the cancellation. Child tasks implicitly link to their parent, meaning their cancellation state links as well.
What is a detached Task? async/await
A detached task in Swift Concurrency is an independent unit of async work that doesn’t inherit priority, actor context, or task-local values from its parent; you use it for fire-and-forget work that must run outside the caller’s context.
Create a detached task async/await
let handle = Task.detached { await compute() }What are the risks of using detached tasks.
They won’t inherit the parent task’s priority and the task-local storage, and they won’t cancel if the parent task gets cancelled.
Summary on Detached Tasks
Detached tasks should be your last resort. Most of the time, you won’t need them, and you’re able to solve the same using regular child tasks, async let, or task groups.
What is async let? async/await
Declares a child task within the current scope that begins running immediately and can be awaited later, allowing structured concurrency.
async let example async/await
async let a = fetchA() async let b = fetchB() let (ra, rb) = await (a, b)
Why TaskGroup? async/await
Dynamically spawn many children and collect results.
Throwing TaskGroup sample async/await
let r = try await withThrowingTaskGroup(of:Int.self){ g in
for n in 1...10 {
g.addTask { try await work(n) }
}
var out:[Int]=[]
for try await x in g {
out.append(x)
}
return out
}What is an actor? async/await
Reference type with isolated mutable state; prevents data races.
What is @MainActor? async/await
Enforces main-thread execution for UI-affecting code.
@MainActor example async/await
@MainActor func updateUI(){
/* UIKit/SwiftUI */
}Define an actor async/await
actor Counter { var value=0; func inc(){ value+=1 } }Call actor from outside async/await
let c = Counter(); await c.inc()
What is nonisolated? async/await
Access without hopping into actor (constants/pure funcs).
nonisolated sample async/await
actor A { nonisolated let id = UUID() }What is Sendable? async/await
Type safe to pass across concurrency domains.