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.
Swift Fundamentals
Write structured asynchronous code with async/await, tasks, actors, cancellation, and Sendable values.
Before you begin
Learning stage
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.
Learning stage
The service performs two independent requests concurrently and updates UI state on the main actor.
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"
}
}Learning stage
These concepts appear throughout production iOS code. Focus on the requirement each technique communicates.
Learning stage
Notice these mistakes early so the compiler and type system can help instead of becoming obstacles.
Learning stage
Work through these activities in a small Xcode project. No embedded playground is required on this site.
Activity 1
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
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.
Learning stage
Answer each question before revealing the explanation.
QUESTION 1
The function may suspend at this point
Suspension lets other work proceed without blocking the underlying thread.
QUESTION 2
It isolates mutable state so concurrent code cannot access it unsafely.
Actor isolation makes access cross an explicit concurrency boundary.
Learning stage
Explain the idea in terms of its purpose, behavior, and tradeoffs rather than reciting syntax.
Short answer: It ties child task lifetime, cancellation, priority, and results to the parent scope.
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
Learning stage
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.