The following demo reveals an inconsistency between the official and backport implementation of task(id:priority:_:).
import Combine
import SwiftUI
import SwiftUIBackports
struct ContentView: View {
@State private var text = "Hello world"
var body: some View {
VStack {
InnerView(text: text)
}
.task {
try? await Task.sleep(nanoseconds: 500_000_000)
print("Will change `text`")
text = "New world"
}
}
}
struct InnerView: View {
let text: String
var body: some View {
VStack {
Text(text)
}
.onChange(of: text) { newValue in
print("[onChange] text: \(text), newValue: \(newValue)")
}
.onReceive(Just(text)) { _ in
print("[onReceive] text: \(text)")
}
.task(id: text) {
print("[task] text: \(text)")
}
.backport.onChange(of: text) { newValue in
print("[backport.onChange] text: \(text), newValue: \(newValue)")
}
.backport.task(id: text) {
print("[backport.task] text: \(text)")
}
}
}
The output is:
[onReceive] text: Hello world
[task] text: Hello world
[backport.task] text: Hello world
Will change `text`
[onChange] text: Hello world, newValue: New world
[onReceive] text: New world
[backport.onChange] text: Hello world, newValue: New world
[backport.task] text: Hello world
[task] text: New world
We can notice that the backport version is still using the old value of text. This is probably because SwiftUI executes onChange(of:perform:) before the value is updated. Since the official implementation of iOS 15 is using the new value, which is the expected behavior, we need to change the backport implementation.
Code changes
We can use Publisher.Just to make the view depend on id, so that SwiftUI would immediately update the value. The code is similar to Backport.onChange(of:perform:).
After the changes, the output is as expected:
[onReceive] text: Hello world
[task] text: Hello world
[backport.task] text: Hello world
Will change `text`
[onChange] text: Hello world, newValue: New world
[onReceive] text: New world
[backport.onChange] text: Hello world, newValue: New world
[backport.task] text: New world
[task] text: New world
Motivation
The following demo reveals an inconsistency between the official and backport implementation of
task(id:priority:_:)
.The output is:
We can notice that the backport version is still using the old value of text. This is probably because SwiftUI executes
onChange(of:perform:)
before the value is updated. Since the official implementation of iOS 15 is using the new value, which is the expected behavior, we need to change the backport implementation.Code changes
We can use
Publisher.Just
to make the view depend onid
, so that SwiftUI would immediately update the value. The code is similar toBackport.onChange(of:perform:)
.After the changes, the output is as expected: