Logo for tanaschita.com

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

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

14 Mar 2022 · 2 min read

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
}
Sponsorship logo
Preparing for a technical iOS job interview
Check out my new book on preparing for a technical iOS job interview with over 200 questions & answers. Test your knowledge on iOS topics such as Swift & Objective-C, SwiftUI & UIKit, Combine, HTTP Networking, Authentication, Core Data, Concurrency with async/await, Security, Automated Testing and more.
LEARN MORE

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.

Sponsorship logo
Preparing for a technical iOS job interview
Check out my new book on preparing for a technical iOS job interview with over 200 questions & answers. Test your knowledge on iOS topics such as Swift & Objective-C, SwiftUI & UIKit, Combine, HTTP Networking, Authentication, Core Data, Concurrency with async/await, Security, Automated Testing and more.
LEARN MORE

Newsletter

Image of a reading marmot
Subscribe

Like to support my work?

Say hi

Related tags

Articles with related topics

async/await

concurrency

swift

How to bridge completions handlers to Swift's async/await

Understand Swift continuations to create your own async/await functions.

05 Dec 2022 · 3 min read

Latest articles and tips

© 2023 tanaschita.com

Privacy policy

Impressum