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}

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.

Newsletter
Like to support my work?
Say hi
Related tags
Articles with related topics
Latest articles and tips