UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

How to create and call an async function

Paul Hudson    @twostraws   

Updated for Xcode 15

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:

  1. Downloading data from the internet should always be done asynchronously, even a very small download can take a long time if the user has a bad cellphone connection.
  2. Doing lots of mathematics might run quickly if the system is doing nothing else, but it might also take a long time if you have complex work and the system is busy doing something else.
  3. Uploading data to the internet suffers from the same networking problems as downloading, and should always be done asynchronously.

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:

  • Every one of those 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.
  • Swift will run each of the await calls in sequence, waiting for the previous one to complete. This is not going to run several things in parallel.
  • Each time an 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.
  • Because it calls async functions using 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.

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Get it on Hacking with Swift+

Sponsor Hacking with Swift and reach the world's largest Swift community!

Similar solutions…

BUY OUR BOOKS
Buy Pro Swift Buy Pro SwiftUI Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let us know!

Average rating: 4.1/5

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.