iOS. Apple. Indies. Plus Things.

Use Swift Concurrency with Completion Handlers

// Written by Jordan Morgan // Sep 17th, 2022 // Read it in about 1 minutes // RE: Snips

This post is brought to you by Emerge Tools, the best way to build on mobile.

let concatenatedThoughts = """

Welcome to Snips! Here, you'll find a short, fully code complete sample with two parts. The first is the entire code sample which you can copy and paste right into Xcode. The second is a step by step explanation. Enjoy!


The Scenario

Convert an existing function that uses a completion handler over to Swift Concurrency.

struct CatFact: Codable {
    let fact: String

// 1
func getCatFact(completion:@escaping ((String) -> Void)) {
    let reqURL = URL(string: "https://catfact.ninja/fact")!
    URLSession.shared.dataTask(with: URLRequest(url: reqURL)) { data, response, error in
        guard let factData = data,
              let catFact = try? JSONDecoder().decode(CatFact.self, from: factData) else {
            completion("No cat facts today.")

// 2
func getCatFact() async -> String {
    // 3
    await withCheckedContinuation { continuation in
        getCatFact { fact in
            continuation.resume(returning: fact)

With that, you can call getCatFact using Swift Concurrency:

let result = await getCatFact()

Instead of this:

getCatFact { result in 

The Breakdown

Step 1
The first step is to realize that you don’t remove your existing function that uses a completion handler. Instead, you need to simply note what return type (if any) the completion handler returns. In this case, it’s just a String.

Step 2
Create a new function that matches the original’s signature, and have its return type match whatever the completion handler returns. Again, that’s a String here. Be sure to add async, and throws if it has error handling.

Step 3
Finally, call the original function within your new async version, and wrap it in a continuation. Within the completion handler, resume it and you’re done! If you’re using error handling, there is a throwing version of a continuation available, too.

Until next time ✌️


Spot an issue, anything to add?

Reach Out.