-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Description
Description
When you use an enum type for State, views aren't updated for changes in the associated value.
A discussion has been opened #2778, but it seems like fixes are yet to come.
I'm creating this issue since I find this a major issue, but the discussion doesn't seem to give it much visibility.
I personally find this a major issue, since states, especially for declarative UIs, are much more unambiguous using sum types instead of product types.
For example:
enum State {
case idle(Int)
case loading(Int)
case loaded(Int, String)
}is much better than
struct State {
var count = 0
var fact: String?
var isLoading = false
}as it precludes the possibility of states that shouldn't exist such as
State(count: 1, fact: "foo", isLoading: true)I remember this working in v1.0.0, but it has stopped working at some version along the way.
Checklist
- I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
- If possible, I've reproduced the issue using the
mainbranch of this package. - This issue hasn't been addressed in an existing GitHub issue or discussion.
Expected behavior
Views should change when associated values of cases change.
Actual behavior
Views aren't changing for changes in associated values, but only for case changes.
Steps to reproduce
The following code demonstrates this:
@Reducer
struct DemoFeature {
@ObservableState
enum State {
case zero
case nonZero(Int)
var value: Int {
get {
switch self {
case .zero: 0
case .nonZero(let value): value
}
}
set {
self = newValue == 0 ? .zero : .nonZero(newValue)
}
}
}
enum Action : Equatable {
case didTapIncrement
case didTapDecrement
case didTapReset
}
var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case .didTapDecrement: state.value -= 1
case .didTapIncrement: state.value += 1
case .didTapReset: state = .zero
}
return .none
}
}
}
struct DemoView : View {
let store: StoreOf<DemoFeature>
var body: some View {
VStack {
Text("\(store.value)")
.font(.largeTitle)
.padding()
.background(.black.opacity(0.1))
.cornerRadius(10)
HStack {
Button("-") {
store.send(.didTapDecrement)
}
.font(.largeTitle)
.padding()
.background(.black.opacity(0.1))
.cornerRadius(10)
Button("+") {
store.send(.didTapIncrement)
}
.font(.largeTitle)
.padding()
.background(.black.opacity(0.1))
.cornerRadius(10)
}
Button("Reset") {
store.send(.didTapReset)
}
.font(.largeTitle)
.padding()
.background(.black.opacity(0.1))
.cornerRadius(10)
}
}
}
#Preview("Demo") {
DemoView(store: Store(initialState: .zero) {
DemoFeature()
})
}The Composable Architecture version information
1.9.2
Destination operating system
iOS 17
Xcode version information
15.0.1
Swift Compiler version information
swift-driver version: 1.87.1 Apple Swift version 5.9 (swiftlang-5.9.0.128.108 clang-1500.0.40.1)
Target: arm64-apple-macosx14.0