How to use async/await in synchronous Swift code with tasks

Learn how to use tasks to call async/await methods from synchronous code.

Natascha Fadeeva
2 min readApr 6, 2022

The async/await feature introduced in Swift 5.5 allows us to write asynchronous code in a shorter and safer way. For a recap on async/await, check out this quick guide on async/await in Swift.

In some situations, we need to call async/await methods or properties from synchronous context. How can we do that?

Let’s look at an example. The following method uses the UNUserNotificationCenter’s async/await methods to ask for authorization to send notifications, then fetches the settings and returns them.

func requestNotificationAuthorization() async throws -> UNNotificationSettings {    let notificationCenter = UNUserNotificationCenter.current()    try await notificationCenter.requestAuthorization(options: [.alert, .sound])    let settings = await notificationCenter.notificationSettings()    return settings}

We would like to call this method from our AppDelegate’s method applicationDidFinishLaunching(_ application: UIApplication).

Let’s try:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {    let settings = try await requestNotificationAuthorization()    return true}

We get the compiler error async call in a function that does not support concurrency. Since the delegate method applicationDidFinishLaunching is not marked with async, we cannot just await a method.

Xcode suggests to add async to the function to resolve the error, but since it’s not our function, we cannot just change it.

That’s where tasks come in. A task is represented by the Task struct in Swift. When using a Task, we provide a closure that can contain synchronous or asynchronous code to perform.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {    Task {       do {            let settings = try await requestNotificationAuthorization()            // Handle settings       } catch {            // Handle error       }   }   return true}

We don’t need to manually start the task, it starts running immediately after creation. We also don’t need to keep a reference to it — only if we need more control over the task like the possibility to cancel.

And that’s basically it. Tasks provide us with an easy way to bridge async/await with other Swift code.

Originally published at https://tanaschita.com.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Natascha Fadeeva
Natascha Fadeeva

Written by Natascha Fadeeva

Writing articles about iOS and Swift programming. Not active here anymore. Checkout tanaschita.com for up-to-date articles.

No responses yet

Write a response