Using semaphore to force to wait for the Task
completion is very risky and I wouldn't use it in the production code.
There is a great blog post about this topic in detail Swift Concurrency Waits for No One
Stick to other well-known tools for that.
For the Button
action case. Personally I prefer to push as long a possible up the chain the need of spinning unstructured Task
. In this case I would rather make a view model with the async
function and spin a task from the View
Button {
Task {
await viewModel.buttonTapped()
}
}
If you need to prevent the re-entrancy you might handle it either by making a guard in
viewModel
(e.g. checking the state or by keeping a reference to a task)
You might also create a custom AsyncButton
reusable component that would have built-in reentrancy prevention + async actions support.
I agree that SwiftUI didn't encounter proper adoption of Swift Concurrency (besides the task
modifier), leading to many (sometimes improper) solutions to the lack of handling async/await by default.
Testing unstructured Task
is also a non-trivial topic