Understanding Swift async/await
Amit Sen
iOS engineer and educator. Helping developers build real-world skills.
Introduction
Swift's async/await makes asynchronous code readable and maintainable. In this post, we'll explore the basics and common patterns.
Why async/await?
Before async/await, we used completion handlers and callbacks. The result was often "callback hell" — nested closures that were hard to follow.
With async/await, you write async code that looks like synchronous code:
func fetchUser() async throws -> User {
let data = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode(User.self, from: data.0)
}
Key Concepts
- async — Marks a function that can suspend
- await — Suspends until the async operation completes
- Task — Creates a new async context
Error Handling
do {
let user = try await fetchUser()
print(user.name)
} catch {
print("Failed to fetch: \(error)")
}
Next Steps
- Convert your completion-handler code to async/await
- Learn about actors for thread-safe state
- Explore structured concurrency with TaskGroup
Want to go deeper? Check out our Swift Concurrency course.

