Convert legacy APIs to async/await

Concurrency with async/await — Part 3

Tim Dolenko
Axel Springer Tech
2 min readAug 15, 2022

--

How to convert callback to async/await?

In the example project, we have 2 classes that represent delegate and callback APIs. Let's see how we provide async/await interface around them.

Here’s our callback API:

Here we just return 3 random emojis to some caller after a delay.

Let’s create a wrapper for this class that would use async/await:

We created an async function, that uses LuckySlotLive combined with withCheckedThrowingContinuation.

withCheckedThrowingContinuation allows us to convert the API. Now we can return whatever is passed inside the closure with return, by using continuation inside the closure. It takes closure and returns the async result!

It’s time to use it inside viewModel:

let slot = LuckySlotAsync() // Now let's use async API!

And update playSlot() to fix compile issues:

Fix the errors in the view to the same way we did before with Task and await!

Update struct asyncyawaituApp: App { ... } to use the correct screen.

//PictureThumbnailsView()
IamFeelingLuckyView()

Run the app! It still works!

But there’s an issue, now we update UI from the background thread! Ooops.

Main Actor

Add @MainActor to the view model's class definition:

@MainActor class IamFeelingLuckyViewModel: ObservableObject {

Run the app! The problem is gone! Adding @MainActor to class definitions moves all properties’ modifications to the main thread. You can also mark with @MainActor separate functions or properties of the class.

How to convert delegate to async/await?

Now let’s convert our delegate API:

After some delay, we will randomly get true or false via didGetLucky(with generator: LuckGenerator, didGetLucky: Bool) delegate function.

Let’s create a wrapper for this class that would use async/await:

We use the same withCheckedContinuation, but now we use a little trick, we actually store it, to make it accessible inside didGetLucky(with:didGetLucky:).

Let’s update the viewModel and the view:

We don’t need delegate no more! Remove extension IamFeelingLuckyViewModel: LuckGeneratorDelegate { ... }. And finally update func playGenerator().

We see each other in Part 4!

--

--