I have come across a strange behavior (or at least one I don’t understand) while trying to cancel a Task. Here is a minimal example: I have a Task that sleeps 30 seconds and then increment a counter.
However, if I call .cancel() on that Task before 30 seconds have passed then the counter is incremented immediately.
I would have expected that cancelling the Task would not increment the counter value; does anyone have an idea of what is going on here?
Thank you!
import SwiftUI
struct ContentView: View {
@State var task: Task<Void, Never>? = nil // reference to the task
@State var counter = 0
var body: some View {
VStack(spacing: 50) {
// display counter value and spawn the Task
Text("counter is (self.counter)")
.onAppear {
self.task = Task {
try? await Task.sleep(nanoseconds: 30_000_000_000)
self.counter += 1
}
}
// cancel button
Button("cancel") {
self.task?.cancel() // <-- when tapped before 30s, counter value increases. Why?
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
4
Answers
How about adding a check for isCancelled like this:
When a task is canceled an error is thrown but you are ignoring the thrown error by using
try?
Here is a variant of your code that will react properly to the cancellation
Placing the code inside a
do catch
block is enough.In SwiftUI it’s best to use the
.task
modifier to use async/await when you want the task lifetime tied to what’s on screen, e.g.