Let's suppose a simple ObservableObjectview model with a few published properties.
class ViewModel: ObservableObject, Equatable {
static func == (lhs: ViewModel, rhs: ViewModel) -> Bool {
lhs.value == rhs.value &&
lhs.anotherProperty1 == rhs.anotherProperty1 &&
lhs.anotherProperty2 == rhs.anotherProperty2 &&
lhs.anotherProperty3 == rhs.anotherProperty3
}
@Published var value = 0
@Published var anotherProperty1 = "foo"
@Published var anotherProperty2 = "bar"
@Published var anotherProperty3 = "baz"
}
I would like onChange to be triggered no matter which ViewModel property is modified.
struct DemoView: View {
@StateObject var viewModel = ViewModel()
var body: some View {
Button {
viewModel.value += 1
} label: {
Text("Increment value \(viewModel.value.formatted())")
}
.onChange(of: viewModel) { _ in debugPrint("viewModel did change") } // FIXME: does not trigger :(
}
}
Here I change value for example. Why is onChange never triggered?
One workaround is to create an additional variable that changes whenever any of the other ones changes.
For example, variable status in the example below will have a different value any time one of the other variables changes. Then, you monitor the changes only for the variable status.
class ViewModel: ObservableObject, Equatable {
static func == (lhs: ViewModel, rhs: ViewModel) -> Bool {
lhs.value == rhs.value &&
lhs.anotherProperty1 == rhs.anotherProperty1 &&
lhs.anotherProperty2 == rhs.anotherProperty2 &&
lhs.anotherProperty3 == rhs.anotherProperty3
}
@Published private(set) var status = UUID()
@Published var value = 0 { didSet { status = UUID() } }
@Published var anotherProperty1 = "foo" { didSet { status = UUID() } }
@Published var anotherProperty2 = "bar" { didSet { status = UUID() } }
@Published var anotherProperty3 = "baz" { didSet { status = UUID() } }
}
In the view:
struct DemoView: View {
@StateObject var viewModel = ViewModel()
var body: some View {
Button {
viewModel.value += 1
} label: {
Text("Increment value \(viewModel.value.formatted())")
}
.onChange(of: viewModel.status) { _ in debugPrint("viewModel did change") }
}
}
Why is onChange never triggered?
Because it's a class (reference type) and the reference of the class (unlike a struct) does not change on any modification of a property.
But maybe you don't understand the functionality if @Published.
You don't need onChange at all because objectWillChange will be called automatically whenever one of the @Published properties changes. That's the main goal of ObservableObject.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With