Combining Async / Await with Closures in Swift

The newish Async / Await changes to the Swift language concurrency protocols which were introduced back in WWDC 2021 are convenient and intuitive options for performing asynchronous operations. In comparison to the older closure completion callbacks, it is a cleaner and a more readable good approach.

New method:​

func fetchData() async throws -> [Data] { 
    // ... 
}

Old method:

func fetchData(completion: (Result<[Data], Error>) -> Void) { 
    // ... 
}

The issue I stumbled upon is attempting to utilize the new method while still having to rely on the old implementation for some part of the functionality. As it turns out, there is a pretty simple option for wrapping the closure inside of the new asynchronous function so it would wait for the  block to be done before returning from the await.

Using Async / Await and Closure Blocks together:

func fetchDataOld(completion: (Result<[Data], Error>) -> Void) { 
    // ... 
} 

func fetchDataNew() async throws -> [Data] { 
    try await withCheckedThrowingContinuation { continuation in 
        fetchDataOld { result in 
            // ... continuation.resume(with: result) 
        } 
    } 
}

What we have here is the old and new request methods. Using the withCheckedContinuation or withCheckedThrowingContinuation (depending whether the top level functions throws) inside of the closure async function will allow to wrap the closure and wait for it to finish execution before continuing.

So now if you’re not in the mood to rewrite legacy code but would like to use the new cool functionality going forward, this is a pretty convenient way of making it all work together.

Hope this helps, happy coding!