I am confused by the onChange
modifier behaviour inside NavigationStack
with single EnvironmentObject
.
I created this simple setup:
struct ContentView: View {
var body: some View {
NavigationStack {
FirstView()
}
.environmentObject(Model())
}
}
struct FirstView: View {
@EnvironmentObject var model: Model
var body: some View {
VStack {
Button("Update") {
model.update(from: Self.self)
}
NavigationLink("Next 1") {
SecondView()
}
}
.onChange(of: model.test) { newValue in
print("(Self.self).test = (newValue)")
}
}
}
struct SecondView: View {
@EnvironmentObject var model: Model
var body: some View {
VStack {
Button("Update") {
model.update(from: Self.self)
}
NavigationLink("Next 2") {
EndView()
}
}
.onChange(of: model.test) { newValue in
print("(Self.self).test = (newValue)")
}
}
}
struct EndView: View {
@EnvironmentObject var model: Model
var body: some View {
VStack {
Button("Update") {
model.update(from: Self.self)
}
Text("End")
}
.onChange(of: model.test) { newValue in
print("(Self.self).test = (newValue)")
}
}
}
class Model: ObservableObject {
@Published var test: String = ""
func update<T>(from _: T.Type) {
print("(T.self).update()")
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [self] in
test = "(T.self).(Date.now.timeIntervalSince1970)"
}
}
}
If I run it, pass through the navigation links and click the Update buttons, I get following logs:
FirstView.update()
FirstView.test = FirstView.1718091409.556666
SecondView.update()
SecondView.test = SecondView.1718091416.5972419
FirstView.test = SecondView.1718091416.5972419
EndView.update()
FirstView.test = EndView.1718091450.701973
EndView.test = EndView.1718091450.701973
This is really strange to me. Why does the onChange
fire inside the FirstView
, but does not fire inside the SecondView
at the same time when update
is called from the EndView
? Is there any further logic to that? I would expect it to either fire in EndView
only or in both of FirstView
and SecondView
. Also can I rely on this behaviour being consistent across much more complicated navigation & environment setups?
Note:
I am using the deprecated version of onChange
because that’s whats supported for my supported version of iOS. I assume that the new one behaves the same way.