Logo for tanaschita.com

How to call async/await functions concurrently in Swift

Learn how to call async/await functions in parallel with tasks.

30 Jun 2021 · 4 min read

As we have learned in this guide on async/await in Swift, calling an asynchronous function with await runs only one piece of code at a time. The caller waits for it to finish before running the next line of code.

Sometimes, we want to be able to start multiple async functions in parallel because they don't depend on each other. This is where tasks come in.

Calling asynchronous functions concurrently with tasks

Tasks are a new feature in Swift version 5.5 that work hand in hand with the new async/await functionality. Each task runs concurrently, so tasks allow us to call asynchronous functions in parallel.

Sponsorship logo
Preparing for a technical iOS job interview
Check out my new book on preparing for a technical iOS job interview with over 200 questions & answers. Test your knowledge on iOS topics such as Swift & Objective-C, SwiftUI & UIKit, Combine, HTTP Networking, Authentication, Core Data, Concurrency with async/await, Security, Automated Testing and more.
LEARN MORE

Instead of directly calling await on a async function, we can use the async-let binding to create a task which provides a new async context for executing code concurrently.

Sequential binding:

let profileImage = await URLSession.shared.data(...)
let headerImage = await URLSession.shared.data(...)

Concurrent binding:

async let profileImageTask = URLSession.shared.data(...)
async let headerImageTask = URLSession.shared.data(...)
let profileImage = await profileImageTask
let headerImage = await headerImageTask

With the concurrent binding approach, we created two child tasks that start independently without waiting for the previous one to complete.

Group tasks

Additionally to async-let tasks, Swift provides group tasks.

Group tasks are designed to provide a dynamic amount of concurrency, i.e. they are perfect for situations, where we don't know the amount of concurrent tasks from the beginning, for example when fetching thumbnails from an array of urls.

func downloadImages(imageURLs: [URL]) async throws -> [UIImage] {
var results: [UIImage] = []
try await withThrowingTaskGroup(of: (Data, URLResponse).self, body: { taskGroup in
for imageURL in imageURLs {
taskGroup.async { try await URLSession.shared.data(from: imageURL) }
}
while let data = try await taskGroup.next(), let image = UIImage(data: data.0) {
results.append(image)
}
})
return results
}

Each task in a group has the same parent task, and each task can have child tasks. Because tasks are arranged in a hierarchy, this approach is called structured concurrency.

Sponsorship logo
Preparing for a technical iOS job interview
Check out my new book on preparing for a technical iOS job interview with over 200 questions & answers. Test your knowledge on iOS topics such as Swift & Objective-C, SwiftUI & UIKit, Combine, HTTP Networking, Authentication, Core Data, Concurrency with async/await, Security, Automated Testing and more.
LEARN MORE

Newsletter

Image of a reading marmot
Subscribe

Like to support my work?

Say hi

Related tags

Articles with related topics

async/await

concurrency

swift

How to bridge completions handlers to Swift's async/await

Understand Swift continuations to create your own async/await functions.

05 Dec 2022 · 3 min read

Latest articles and tips

© 2023 tanaschita.com

Privacy policy

Impressum