Below is simplified State for a TCA App. (Assume the rest of the stack is compiling.)
//PROBLEM: When Dog is a Class, instead of a Struct, changes to the dog name
property made in the Reducer fail to update the UI
class Dog: Equatable {
var name = "Spot"
//Equatable conformance
static func == (lhs: Dog, rhs: Dog) -> Bool {
lhs.name == rhs.name
}
}
struct DogManager: ReducerProtocol {
struct State {
var dog = Dog()
}
}
//Action enum and reduce method omitted
}
The question is why UI updates are not made when state changes are made in the reduce
method to the dog
property.
//in the 'reduce' method of a ReducerProtocol-conforming Struct
case .changeDogName: state.dog.name = "Fido"
//changing the property 'dog.name' (assuming 'dog' is a reference-type object)
//does *not* trigger view updates
Even when the Dog class is decorated with ObservableObject
and the name
property with the @Published
property-wrapper, UI updates do not occur when dog.name
is modified in the reduce
method.
I understand the TCA library encourages us to put everything State-related into a Struct. But is it possible at all to use reference-type properties, and have SwiftUI updates work properly?
I know there are workarounds:
- Make Dog a Struct, instead of a Class. Then everything works as expected.
- Add Dog into a ReducerProtocol-conforming Struct as a separate
ObservedObject
property. Then UI updates also work as expected.
I'm just curious why UI updates don't work when a reference type is added as a property in a State struct, and then modified by the reducer. I would have thought that Equatable
conformance by a Class would be sufficient to trigger view updates.