Common SwiftUI pitfalls - onChange modifier + Task
When developing in SwiftUI, it is common to encounter scenarios where asynchronous tasks need to be executed upon specific state changes. It may be intuitive to implement the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
struct MyView: View {
@State private var myState: MyState = ...
var body: some View {
MyAwesomeView()
.onChange(of: myState) {
Task {
await doWork()
}
}
}
}
However, this approach generates a new unstructured task each time myState
changes, without a mechanism to cancel it. While you could manage task cancellation by storing the task in a variable and cancelling it in the subsequent onChange
closure, a more streamlined solution exists:
1
2
3
4
5
6
7
8
9
10
11
struct MyView: View {
@State private var myState: MyState = ...
var body: some View {
MyAwesomeView()
.task(id: myState) {
await doWork()
}
}
}
This method effectively cancels and recreates the task upon myState
changes, and ensuring task cancellation when the view disappears.
Please note that the task will also trigger when the view appears.
This post is licensed under CC BY 4.0 by the author.