BindingAction pattern matching fails when using Binding.pullback #842
Replies: 3 comments 2 replies
-
Digging into this a bit deeper, the reason this fails is because the key path given to the case pattern does not equal the key path of the binding action produced using let keyPath: WritableKeyPath<State, BindableState<String>> = \.$value
let bindingAction: BindingAction<State> = .set(\.$value, "a")
XCTAssertNoDifference(keyPath, bindingAction.keyPath) // pass
let viewBindingAction: BindingAction<ViewState> = .set(\.$value, "a")
let bindingActionTwo: BindingAction<State> = viewBindingAction.pullback(\.viewState)
XCTAssertNoDifference(keyPath, bindingActionTwo.keyPath) // fail I'm not sure exactly how key path equality is determined by swift - it looks like However when you use You can see this is true because I can get the above failing test to pass by adding a new case to the reducer: let reducer = Reducer<State, Action, ()> { state, action, _ in
switch action {
case .binding(\.$value):
state.count += 1
return .none
case .binding(\.viewState.$value): // this will match the pulled back action
state.count += 1
return .none
default:
return .none
}
} Unfortunately whilst this is a potential workaround for handling patterns inside your feature domain, its not helpful when you want to pattern match on a feature's action in a reducer higher up the tree if your feature is in it's own module and doesn't expose the internals of it its nested view states publicly. For now I've had to resort to using normal actions and bindings in my nested view state/actions and mapping them directly to a binding action on my feature domain. |
Beta Was this translation helpful? Give feedback.
-
@lukeredpath We actually had a conversation about this on Twitter shortly after the latest binding updates: https://twitter.com/pointfreeco/status/1440421625690681358 It's a bit of a gotcha, but I think this has been the behavior from the beginning, when There are workarounds that don't involve introducing view state in the reducer, but they all have their trade-offs. For example you could use Brandon and I have even discussed dropping There might be a better formulation of binding actions + view state than the public struct LoginView: View {
let store: Store<LoginState, LoginAction>
struct ViewState: Equatable {
var alert: AlertState<LoginAction>?
@BindableViewState(\LoginState.$email) var email: String We're not sure if it's possible, though, and we probably won't find the time to experiment with it anytime soon. If you (or another intrepid member of the community) is interested in running with this idea and seeing if there's a potential solution there, please keep us posted! I'm also going to convert this to a discussion since it's not really a bug, just a surprising behavior that should either be documented (or removed). |
Beta Was this translation helpful? Give feedback.
-
I recently ran into this problem as well. @stephencelis I'm interested in the idea of |
Beta Was this translation helpful? Give feedback.
-
Describe the bug
When using pattern matching in a reducer to layer additional behaviour on a binding action, the pattern match fails if the binding action was pulled back from a child binding action.
To Reproduce
Failing test case added to a branch on my fork here:
lukeredpath@a6472a1
Expected behavior
In the above failing test I'd expect the count to be 2 but it is not because the reducer does not match the binding action case.
Environment
Beta Was this translation helpful? Give feedback.
All reactions