Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the correct way to await the completion of two Tasks in Swift 5.5 in a function that does not support concurrency?

I have an app that does some processing given a string, this is done in 2 Tasks. During this time i'm displaying an animation. When these Tasks complete i need to hide the animation. The below code works, but is not very nice to look at. I believe there is a better way to do this?

let firTask = Task {
    /* Slow-running code */
}

let airportTask = Task {
    /* Even more slow-running code */
}

Task {
    _ = await firTask.result
    _ = await airportTask.result
    
    self.isVerifyingRoute = false
}
like image 419
Maciej Swic Avatar asked Sep 11 '25 14:09

Maciej Swic


1 Answers

Isn't the real problem that this is a misuse of Task? A Task, as you've discovered, is not really of itself a thing you can await. If the goal is to run slow code in the background, use an actor. Then you can cleanly call an actor method with await.

let myActor = MyActor()
await myActor.doFirStuff()
await myActor.doAirportStuff()
self.isVerifyingRoute = false

However, we also need to make sure we're on the main thread when we talk to self — something that your code omits to do. Here's an example:

actor MyActor {
    func doFirStuff() async {
        print("starting", #function)
        await Task.sleep(2 * 1_000_000_000)
        print("finished", #function)
    }
    func doAirportStuff() async {
        print("starting", #function)
        await Task.sleep(2 * 1_000_000_000)
        print("finished", #function)
    }
}
func test() {
    let myActor = MyActor()
    Task {
        await myActor.doFirStuff()
        await myActor.doAirportStuff()
        Task { @MainActor in
            self.isVerifyingRoute = false
        }
    }
}

Everything happens in the right mode: the time-consuming stuff happens on background threads, and the call to self happens on the main thread. A cleaner-looking way to take care of the main thread call, in my opinion, would be to have a @MainActor method:

func test() {
    let myActor = MyActor()
    Task {
        await myActor.doFirStuff()
        await myActor.doAirportStuff()
        self.finish()
    }
}
@MainActor func finish() {
    self.isVerifyingRoute = false
}

I regard that as elegant and clear.

like image 195
matt Avatar answered Sep 13 '25 06:09

matt