EXPEDIA GROUP TECHNOLOGY — ENGINEERING

Async/Await in Swift

Improving concurrent code

Photo by Christina @ wocintechchat.com on Unsplash

During WWDC 2021, Apple introduced a great improvement for asynchronous programming: async and await. Those two keywords will already be familiar to anyone who has written asynchronous code in JavaScript. Let’s dive into the following example to understand more about how to use them.

Problem area

Let’s develop a tour and travel application. One simple feature is that users can cancel and track refund status for their trip. We can call an API to get all the user’s trip lists followed by another API call to get refund status for each trip. Let’s write some code and see it in action!

Trip details model

In the following snippet, we create a model to store trip details. We can review status to know whether a trip is cancelled, confirmed or booked.

///  Model to store Trip Detailsstruct Trip: Codable {  let id: String  let itinerary: String  let status: TripStatus  enum TripStatus: String, Codable {    case confirmed    case cancelled    case booked    case unknown  }
}

Let’s implement our desired feature using closures.

Trailing closures

func getAllCancelledTrips(for userId: String, completion: @escaping ([Trip]) -> Void) {  APIManager.shared.getAllTrips(for: userId) { [weak self] trips in    guard let self = self else { return }    let cancelledTrips = trips.filter { $0.status == .cancelled }      completion(cancelledTrips)  }}

What is happening above?

  1. We call an API to get all the trips for the given user ID.
  2. Next, we filter out cancelled trips and returned the response using the completion block.

That code looks fine, but what if we need multiple nested closures?

Nested closures

In the following example, after fetching cancelled trips we call a separate API to get refund status of the trip. For this simple task, we have two nested closures. Multiple nested closures make the code look messy and hard to understand.

APIManager.shared.getAllTrips(for: userId) {​​​​​​ [weak self] trips in  guard let self = self else {​​​​​​ return }​​​​​​  let cancelledTrips = trips.filter {​​​​​​ $0.status == .cancelled }​​​​​​  for trip in cancelledTrips {​​​​​​    APIManager.shared.getRefundStatus(for: trip.id) {​​​​​​ isRefunded in      /// Do necessary stuffs
}​​​​​​
}​​​​​ completion(cancelledTrips)}​​​​​​

Calling async and await to the rescue

Let’s now see how async/await can simplify the previous code!

Calling function

We add async right before the return type in the function definition. This indicates that something asynchronous happens within the function.

/// Calling functionfunc getAllTrips(for userId: String) async -> [Trip] {  /// Method definition goes here}

Caller function

We have to use the await keyword in front of the caller function. By doing this, the current execution will be paused until the result is obtained.

/// Caller functionfunc getAllCancelledTrips(for userId: String) async -> [Trip] {  let trips = await APIManager.shared.getAllTrips(for: userId)  let cancelledTrips = trips.filter { $0.status == .cancelled }  return cancelledTrips}

Parallel execution

The previous example shows the use case of serial execution. If we want to execute this call asynchronously we can simply write async before the variable name. Consequently, we need to add the await keyword whenever we refer to that variable, like where we filter the full list of trips.

func getAllCancelledTrips(for userId: String) async -> [Trip] {  /// Added async keyword to allow asynchronous call of the 
/// following method
async let trips = await APIManager.shared.getAllTrips(for: userId) let cancelledTrips =
await trips.filter { $0.status == .cancelled }
return cancelledTrips}func getRefundStatusFor(userId: String) async { async let cancelledTrips = await getAllCancelledTrips(for: userId) for trip in await cancelledTrips { APIManager.shared.getRefundStatus(for: trip.id) { isRefunded in // Do necessary stuff }
}
}

To summarize the advantages of using async/await:

  • Less code
  • Easier to read
  • Simple

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store