Introduction
Starting with Swift 5.5, the language introduced a native concurrency model built around async and await. This replaces much of the older completion-handler and Grand Central Dispatch (GCD) style code with straight-line, readable syntax for asynchronous work, while still running efficiently on a cooperative thread pool. Alongside async/await, Swift added Task for launching units of asynchronous work and actors for protecting mutable state from data races in concurrent code.
Cricket analogy: Swift 5.5's async/await is like replacing a chain of walkie-talkie callbacks between the umpire, scorer, and broadcast van with a single straight-line script that just 'awaits' each confirmation in order; a Task {} launches a new replay-review process, and an actor is like the official scorer who's the only one allowed to touch the scorebook at a time, preventing two officials from editing the same over simultaneously.
Syntax
func fetchData() async throws -> Data {
let (data, _) = try await URLSession.shared.data(from: url)
return data
}
Task {
do {
let data = try await fetchData()
print("Fetched \(data.count) bytes")
} catch {
print("Fetch failed: \(error)")
}
}Explanation
A function marked async can suspend execution at an await point without blocking the underlying thread, allowing other work to run while it waits. Calling an async function requires the await keyword, and if the function is also throws, the call is written try await. Task { ... } creates a new asynchronous context, often used to bridge synchronous code (like a button action) into the async world. Because async functions can suspend and resume on different threads, shared mutable state can still race; actors solve this by serializing all access to their mutable properties, so only one task can touch that state at a time.
Cricket analogy: Fetching live ball-by-ball data with await lets the app suspend at that point without freezing the whole broadcast thread, and if the fetch can fail it's written try await; tapping a 'refresh score' button triggers a Task { } to bridge that synchronous tap into the async fetch, and the shared scorecard actor serializes updates so two feeds can't edit the same over at once.
Example
actor DataStore {
private var cache: [String: Int] = [:]
func set(_ key: String, _ value: Int) {
cache[key] = value
}
func get(_ key: String) -> Int? {
cache[key]
}
}
func fetchScore() async -> Int {
try? await Task.sleep(nanoseconds: 200_000_000)
return 42
}
let store = DataStore()
Task {
let score = await fetchScore()
await store.set("level1", score)
let saved = await store.get("level1")
print("Saved score: \(saved ?? -1)")
}Output
Saved score: 42Key Takeaways
- Swift's async/await was introduced in Swift 5.5 and lets asynchronous code read like straight-line synchronous code.
- Functions that suspend are marked async and are called with await; combined with throws, calls use try await.
- Task { ... } launches a unit of asynchronous work, often bridging synchronous contexts into async code.
- async/await largely replaces older completion-handler and GCD-based patterns for many common asynchronous tasks.
- actors serialize access to their mutable state, preventing data races without manual locking.
- Accessing an actor's members from outside requires await, since the actor may need to queue the request.
Practice what you learned
1. In which Swift version was the async/await concurrency model introduced?
2. What keyword is required when calling an async function?
3. What is the purpose of Task { ... } in Swift concurrency?
4. What problem do actors solve in Swift concurrency?
5. How would a throwing async function typically be called?
Was this page helpful?
You May Also Like
Error Handling in Swift
Learn how Swift represents, throws, propagates, and handles runtime errors using the Error protocol and try/catch.
Closures in Swift
Understand closures as self-contained blocks of functionality that capture and store references to surrounding variables.
Functions in Swift
Learn how to declare, call, and label the parameters of reusable blocks of code called functions in Swift.
Memory Management and ARC in Swift
Understand how Automatic Reference Counting manages class instance memory in Swift and how to avoid retain cycles.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics