Updated for Xcode 14.2
Using async functions in Swift is done in two steps: declaring the function itself as being async
, then calling that function using await
.
For example, if we were building an app that wanted to download a whole bunch of temperature readings from a weather station, calculate the average temperature, then upload those results, then we might want to make all three of those async:
To actually use those functions we would then need to write a fourth function that calls them one by one and prints the response. This function also needs to be async, because in theory the three functions it calls could suspend and so it might also need to be suspended.
I’m not going to do the actual networking code here, because we’ll be looking a lot at networking later on. Instead, I want to focus on the structure of our functions so you can see how they fit together, so we’ll be using mock data here – random numbers for the weather data, and the string “OK” for our server response.
Here’s the code:
func fetchWeatherHistory() async -> [Double] {
(1...100_000).map { _ in Double.random(in: -10...30) }
}
func calculateAverageTemperature(for records: [Double]) async -> Double {
let total = records.reduce(0, +)
let average = total / Double(records.count)
return average
}
func upload(result: Double) async -> String {
"OK"
}
func processWeather() async {
let records = await fetchWeatherHistory()
let average = await calculateAverageTemperature(for: records)
let response = await upload(result: average)
print("Server response: \(response)")
}
await processWeather()
Download this as an Xcode project
So, we have three simple async functions that fit together to form a sequence: download some data, process that data, then upload the result. That all gets stitched together into a cohesive flow using the processWeather()
function, which can then be called from elsewhere.
That’s not a lot of code, but it is a lot of functionality:
await
calls is a potential suspension point, which is why we marked it explicitly. Like I said, one async function can suspend as many times as is needed.await
calls in sequence, waiting for the previous one to complete. This is not going to run several things in parallel.await
call finishes, its final value gets assigned to one of our constants – records
, average
, and response
. Once created this is just regular data, no different from if we had created it synchronously.await
, it is required that processWeather()
be itself an async function. If you remove that Swift will refuse to build your code.When reading async functions like this one, it’s good practice to look for the await
calls because they are all places where unknown other amounts of work might take place before the next line of code executes.
Think of it a bit like this:
func processWeather() async {
let records = await fetchWeatherHistory()
// anything could happen here
let average = await calculateAverageTemperature(for: records)
// or here
let response = await upload(result: average)
// or here
print("Server response: \(response)")
}
We’re only using local variables inside this function, so they are safe. However, if you were relying on properties from a class, for example, they might have changed between each of those await
lines.
Swift provides ways of protecting against this using a system known as actors – more on that much later.
SAVE 50% To celebrate WWDC23, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.
Link copied to your pasteboard.