I have this situation where I can push two different view controllers from a given view controller, the first one will be pushed immediately, the second one after an API call, so to mimic it I have written this:
class TestViewController: UIViewController {
@IBAction func pushFirstVC() {
DispatchQueue.main.async { [weak self] in
weakSelf?.navigationController?.pushViewController(FirstViewController(), animated: true)
}
}
@IBAction func pushSecondVC() {
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) { [weak self] in
self?.navigationController?.pushViewController(SecondViewController(), animated: true)
}
}
}
Now let's say the user first triggers the pushSecondVC() event, then during the 5 next five seconds he triggers the pushFirstVC() - and then I would like to definitely cancel the initial pushSecondVC() in order not to push a SecondViewController instance, even if the user pops back to TestViewController in this 5 seconds delay.
I have tried several things, like adding a canPush flag that becomes true in viewDidAppear and false in viewDidDisappear. But this doesn't prevent the push view controller from happening if the user came back in TestViewController in the 5 seconds delay. So what I would like to do would be to specifically cancel the expected task, but I have no idea how to do so.
Thank you for your help
Grand Central Dispatch doesn't offer a way to cancel a block after it's been Queued. There is a similar mechanism,
NSOperationwhich does provide a way to cancel a pending operation. Unfortunately, I don't have any source code handy.But even easier than that might be to add a "cancel" flag and check if you've canceled something before the pop happens.
And finally, if you look at Swift Structured Concurrency, there is a concept of a tree of asynchronous tasks. With the tree, if you cancel a task that owns sub-tasks, then the sub-tasks can determine if they've been cancelled and skip work that is no longer needed.
For more info, watch Explore structured concurrency in Swift