Swift's concurrency model replaces callback pyramids with readable, structured code. Here's the practical subset you'll reach for every day.
async / await
Mark a function async, then await its result. The compiler handles the suspension points.
func loadProfile(id: String) async throws -> Profile {
let (data, _) = try await URLSession.shared.data(from: profileURL(id))
return try JSONDecoder().decode(Profile.self, from: data)
}Structured Concurrency
Run child tasks in parallel and gather results with async let or a task group — cancellation
propagates automatically.
async let profile = loadProfile(id: userID)
async let feed = loadFeed(for: userID)
let dashboard = try await Dashboard(profile: profile, feed: feed)Actors Keep State Safe
An actor serializes access to its mutable state, eliminating data races by construction.
actor ImageCache {
private var store: [URL: Image] = [:]
func image(for url: URL) -> Image? { store[url] }
func insert(_ image: Image, for url: URL) { store[url] = image }
}Watch out
Hopping onto an actor has a cost. Don't wrap tiny, hot values in an actor when a simpler synchronization primitive will do.
Key Takeaways
Reach for async/await for clarity, structured concurrency for parallelism, and actors for
shared mutable state. Together they make correct concurrent code the default.