Friday, April 17, 2026
HomeiOS DevelopmentIntroduction to async/await in Swift

Introduction to async/await in Swift

[ad_1]

The principle mission

Swift 5.5 accommodates lots of new options, most of them is all about “a greater concurrency mannequin” for the language. The very first step into this new asynchronous world is a correct async/await system.


In case you are interested by these new experimental API’s you need to obtain the most recent Swift 5.5 improvement snapshot from the swift.org/obtain web page. In case you are utilizing Xcode, please do not forget to pick out the right toolchain utilizing the preferences / elements tab.


In fact you may nonetheless use common completion blocks or the Dispatch framework to put in writing async code, however looks like the way forward for Swift entails a local method to deal with concurrent duties even higher. There’s mix as nicely, however that is solely accessible for Apple platforms, so yeah… 🥲


Let me present you easy methods to convert your outdated callback & consequence kind primarily based Swift code right into a shiny new async/await supported API. First we’re going to create our experimental async SPM mission.


import PackageDescription

let bundle = Bundle(
    title: "AsyncSwift",
    merchandise: [
        .executable(name: "AsyncSwift", targets: ["AsyncSwift"])
    ],
    dependencies: [
        
    ],
    targets: [
        .executableTarget(name: "AsyncSwift",
                          swiftSettings: [
                            .unsafeFlags([
                                "-parse-as-library",
                                "-Xfrontend", "-disable-availability-checking",
                                "-Xfrontend", "-enable-experimental-concurrency",
                            ])
                          ]
        ),
        .testTarget(title: "AsyncSwiftTests", dependencies: ["AsyncSwift"]),
    ]
)


You may need seen that we’re utilizing the most recent swift-tools-version:5.4 and we added just a few unsafe flags for this mission. It’s because we’ll use the brand new @foremost attribute contained in the executable bundle goal, and the concurrency API requires the experimental flag to be current.


Now we should always create a foremost entry level inside our foremost.swift file. Since we’re utilizing the @foremost attribute it’s doable to create a brand new struct with a static foremost methodology that may be routinely launched whenever you construct & run your mission utilizing Xcode or the command line. 🚀


@foremost
struct MyProgram {

    static func foremost() {
        print("Howdy, world!")
    }
}


Now that we have now a clear foremost entry level, we should always add some commonplace URLSession associated performance that we’re going to change with new async/await calls as we refactor the code.

We’re going name our normal pattern todo service and validate our HTTP response. To get extra particular particulars of a doable error, we are able to use a easy HTTP.Error object, and naturally as a result of the dataTask API returns instantly we have now to make use of the dispatchMain() name to attend for the asynchronous HTTP name. Lastly we merely swap the consequence kind and exit if wanted. ⏳


import Basis
import _Concurrency  

enum HTTP {
    enum Error: LocalizedError {
        case invalidResponse
        case badStatusCode
        case missingData
    }
}

struct Todo: Codable {
    let id: Int
    let title: String
    let accomplished: Bool
    let userId: Int
}

func getTodos(completion: @escaping (Consequence<[Todo], Error>) -> Void) {
    let req = URLRequest(url: URL(string: "https://jsonplaceholder.typicode.com/todos")!)
    let job = URLSession.shared.dataTask(with: req) { knowledge, response, error in
        guard error == nil else  {
            return completion(.failure(error!))
        }
        guard let response = response as? HTTPURLResponse else {
            return completion(.failure(HTTP.Error.invalidResponse))
        }
        guard 200...299 ~= response.statusCode else {
            return completion(.failure(HTTP.Error.badStatusCode))
        }
        guard let knowledge = knowledge else {
            return completion(.failure(HTTP.Error.missingData))
        }
        do {
            let decoder = JSONDecoder()
            let todos = strive decoder.decode([Todo].self, from: knowledge)
            return completion(.success(todos))
        }
        catch {
            return completion(.failure(error))
        }
    }
    job.resume()
}

@foremost
struct MyProgram {

    static func foremost() {
        getTodos { consequence in
            swap consequence {
            case .success(let todos):
                print(todos.depend)
                exit(EXIT_SUCCESS)
            case .failure(let error):
                fatalError(error.localizedDescription)
            }
            
        }
        dispatchMain()
    }
}

Should you bear in mind I already confirmed you the Mix model of this URLSession knowledge job name some time again, however as I discussed this Mix will not be solely accessible for iOS, macOS, tvOS and watchOS.


Async/await and unsafe continuation

So how will we convert our current code into an async variant? Nicely, the excellent news is that there’s a methodology referred to as withUnsafeContinuation that you should utilize to wrap current completion block primarily based calls to supply async variations of your capabilities. The fast and soiled resolution is that this:


import Basis
import _Concurrency

 

func getTodos() async -> Consequence<[Todo], Error> {
    await withUnsafeContinuation { c in
        getTodos { consequence in
            c.resume(returning: consequence)
        }
    }
}

@foremost
struct MyProgram {

    static func foremost() async {
        let consequence = await getTodos()
        swap consequence {
        case .success(let todos):
            print(todos.depend)
            exit(EXIT_SUCCESS)
        case .failure(let error):
            fatalError(error.localizedDescription)
        }
    }
}


The continuations proposal was born to supply us the mandatory API to work together with synchronous code. The withUnsafeContinuation perform provides us a block that we are able to use to renew with the generic async return kind, this fashion it’s ridiculously simple to quickly write an async model of an current the callback primarily based perform. As at all times, the Swift developer staff did an incredible job right here. 👍


One factor you may need seen, that as an alternative of calling the dispatchMain() perform we have modified the primary perform into an async perform. Nicely, the factor is which you could’t merely name an async perform inside a non-async (synchronous) methodology. ⚠️


Interacting with sync code

With the intention to name an async methodology inside a sync methodology, you need to use the brand new detach perform and you continue to have to attend for the async capabilities to finish utilizing the dispatch APIs.


import Basis
import _Concurrency



@foremost
struct MyProgram {

    static func foremost() {
        detach {
            let consequence = await getTodos()
            swap consequence {
            case .success(let todos):
                print(todos.depend)
                exit(EXIT_SUCCESS)
            case .failure(let error):
                fatalError(error.localizedDescription)
            }
        }
        dispatchMain()
    }
}


In fact you may name any sync and async methodology inside an async perform, so there are not any restrictions there. Let me present you yet another instance, this time we’ll use the Grand Central Dispatch framework, return just a few numbers and add them asynchronously.


Serial vs concurrent execution


Think about a typical use-case the place you need to mix (pun supposed) the output of some lengthy operating async operations. In our instance we’ll calculate some numbers asynchronously and we might wish to sum the outcomes afterwards. Let’s study the next code…


import Basis
import _Concurrency

func calculateFirstNumber() async -> Int {
    print("First quantity is now being calculated...")
    return await withUnsafeContinuation { c in
        DispatchQueue.foremost.asyncAfter(deadline: .now() + 2) {
            print("First quantity is now prepared.")
            c.resume(returning: 42)
        }
    }
}

func calculateSecondNumber() async -> Int {
    print("Second quantity is now being calculated...")
    return await withUnsafeContinuation { c in
        DispatchQueue.foremost.asyncAfter(deadline: .now() + 1) {
            print("Second quantity is now prepared.")
            c.resume(returning: 6)
        }
    }
}

func calculateThirdNumber() async -> Int {
    print("Third quantity is now being calculated...")
    return await withUnsafeContinuation { c in
        DispatchQueue.foremost.asyncAfter(deadline: .now() + 3) {
            print("Third quantity is now prepared.")
            c.resume(returning: 69)
        }
    }
}

@foremost
struct MyProgram {

    static func foremost() async {
        let x = await calculateFirstNumber()
        let y = await calculateSecondNumber()
        let z = await calculateThirdNumber()
        print(x + y + z)
    
}


As you may see these capabilities are asynchronous, however they’re nonetheless executed one after one other. It actually would not matter for those who change the primary queue into a special concurrent queue, the async job itself will not be going to fireside till you name it with await. The execution order is at all times serial. 🤔


Spawn duties utilizing async let


It’s doable to alter this habits through the use of the model new async let syntax. If we transfer the await key phrase only a bit down the road we are able to fireplace the async duties straight away by way of the async let expressions. This new characteristic is a part of the structured concurrency proposal.




@foremost
struct MyProgram {

    static func foremost() async {
        async let x = calculateFirstNumber()
        async let y = calculateSecondNumber()
        async let z = calculateThirdNumber()

        let res = await x + y + z
        print(res)
    }
}


Now the execution order is concurrent, the underlying calculation nonetheless occurs in a serial approach on the primary queue, however you have bought the concept what I am making an attempt to indicate you right here, proper? 😅

Anyway, merely including the async/await characteristic right into a programming language will not remedy the extra advanced points that we have now to cope with. Thankfully Swift could have nice assist to async job administration and concurrent code execution. I am unable to wait to put in writing extra about these new options. See you subsequent time, there’s a lot to cowl, I hope you will discover my async Swift tutorials helpful. 👋


[ad_2]

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments