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

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.

Image of books
Further reading: Swift Power Pack
For further reading, check out the Swift Power Pack books by Paul Hudson. The bundle delivers six books from beginner to advanced around Swift topics.


Receive a monthly newsletter about the latest articles and tips.

Image of a reading marmot

Related tags

Written by

Articles with related topics

Latest articles and tips