Let’s learn Swift’s async/await from the beginning, assuming you have basic knowledge of Swift but are new to asynchronous programming.
First, understand synchronous vs. asynchronous
Synchronous means one operation finishes before the next operation begins.
print("1. Start")
print("2. Process data")
print("3. Finish")Output:
1. Start 2. Process data 3. Finish
Each statement executes in order. Asynchronous means an operation can take time, and your program can suspend that operation while other work continues. For example, when an iOS app downloads data from a server, the internet request might take one or two seconds. You don’t want the app’s interface to freeze while waiting. That’s where async/await helps.
What does async mean?
The async keyword tells Swift that a function can perform asynchronous work.
func fetchData() async {
print("Fetching data")
}Here, async declares an asynchronous function. You cannot call this function directly from an ordinary synchronous function like this:
fetchData() // Error in a synchronous context
You need an asynchronous context to call it.
What does await mean?
The await keyword is used when calling an asynchronous operation that may suspend.
func fetchData() async {
print("Start")
await performOperation()
print("Finish")
}
func performOperation() async {
print("Processing")
}When fetchData() runs, the statements execute in order:
- Print
Start. - Call
performOperation()and wait for it to complete. - Print
Finish.
Here, await doesn’t block the underlying thread while the task is suspended. It allows Swift to suspend the current task until the awaited operation can return.
Why do we need Task?
Suppose you have a regular function:
func viewDidLoad() {
fetchData()
}This won’t compile because viewDidLoad() is synchronous and fetchData() is asynchronous.
You can use Task to start asynchronous work:
func viewDidLoad() {
Task {
await fetchData()
}
}Think of the keywords this way:
async— declares a function that supports asynchronous execution.await— calls an asynchronous operation.Task— provides a context in which asynchronous work can run.
What does throws mean?
An asynchronous operation can fail. For example, a network request might encounter a connection error.
Swift allows an asynchronous function to throw errors:
func fetchData() async throws -> String {
return "Data received"
}Let’s understand the declaration:
async: the function supports asynchronous execution.throws: the function can fail by throwing an error.-> String: the function returns a string on success.
To call it, you generally use try await:
Task {
do {
let result = try await fetchData()
print(result)
} catch {
print("Error:", error)
}
}try handles the throwing nature of the call, await handles the asynchronous nature, and do-catch lets you respond to failures.
Remember these three rules
async -> Declares an asynchronous function
await -> Marks a potentially suspending operation
try -> Calls a throwing operation
You’ll commonly see them together:
let data = try await fetchData()
Read this as: call the asynchronous function, wait for its result without blocking the thread, and allow errors to be thrown.
