Convert legacy APIs to async/await
Concurrency with async/await — Part 3
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!