Async await is part of the new structured concurrency changes that arrived in Swift 5.5 during WWDC 2021. Concurrency in Swift means allowing multiple pieces of code to run at the same time. This is a very simplified description, but it should give you an idea already how important concurrency in Swift is for the performance of your apps. With the new async methods and await statements, we can define methods performing work asynchronously.
You might have read about the Swift Concurrency Manifesto by Chris Lattner before, which was announced a few years back. Many developers in the Swift community are excited about the future to come with a structured way of defining asynchronous code. Now that it’s finally here, we can simplify our code with async-await and make our asynchronous code easier to read.
Learn about the 3-step process to transition your iOS development to Apple M1 silicon Macs — with insights from Reddit’s successful M1 transition, FAQs, and helpful M1 transition tips.
Async stands for asynchronous and can be seen as a method attribute making it clear that a method performs asynchronous work. An example of such a method looks as follows:
func fetchImages() async throws -> [UIImage] {
// .. perform data request
}
The fetchImages method is defined as async throwing, which means that it’s performing a failable asynchronous job. The method would return a collection of images if everything went well or throws an error if something went wrong.
Async methods replace the often seen closure completion callbacks. Completion callbacks were common in Swift to return from an asynchronous task, often combined with a Result type parameter. The above method would have been written as followed:
func fetchImages(completion: (Result<[UIImage], Error>) -> Void) {
// .. perform data request
}
Defining a method using a completion closure is still possible in Swift today, but it has a few downsides that are solved by using async instead:
These downsides are based on the closure version using the relatively new Result enum. It’s likely that a lot of projects still make use of completion callbacks without this enumeration:
func fetchImages(completion: ([UIImage]?, Error?) -> Void) {
// .. perform data request
}
Defining a method like this makes it even harder to reason about the outcome on the caller’s side. Both value and error are optional, which requires us to perform an unwrap in any case. Unwrapping these optionals results in more code clutter which does not help to improve readability.
Await is the keyword to be used for calling async methods. You can see them as best friends in Swift as one will never go without the other. You could basically say:
“Await is awaiting a callback from his buddy async”
Even though this sounds childish, it’s not a lie! We could take a look at an example by calling our earlier defined async throwing fetch images method: