CaseLetStore (for example) #388
Replies: 4 comments 12 replies
-
AFAIK there's a branch called |
Beta Was this translation helpful? Give feedback.
-
I really like the idea! I think it's a worthy addition for the already existing wrapper on result builder abilities. We have I think it's gonna be impossible to provide an API for such store that supports any number of case paths because of the lack of variadic generics in Swift. Same reason why ViewBuilder has all the overloads for But maybe it's not really a must? You could provide a view for just a single case path and the user, if they need to cover more paths, will just write them one by one like: CaseStore(testStore, path: /TestState.first) { (store: Store<String, TestAction>) in
WithViewStore(store) { (viewStore: ViewStore<String, TestAction>) in
Text("first: \(viewStore.state)")
}
}
CaseStore(testStore, path: /TestState.second) { (store: Store<Int, TestAction>) in
WithViewStore(store) { (viewStore: ViewStore<Int, TestAction>) in
Text("second: \(viewStore.state)")
}
} with the naiive implementation of public struct CaseStore<State, Action, InnerState, Content>: View where Content: View {
private let content: (ViewStore<State, Action>) -> Content?
private let store: Store<State, Action>
public init(
_ store: Store<State, Action>,
path: CasePath<State, InnerState>,
content: @escaping (Store<InnerState, Action>) -> Content
) {
self.store = store
self.content = { viewStore in
ViewBuilder.buildIf(path.extract(from: viewStore.state).map { state in content(store.scope(state: { _ in state })) })
}
}
public var body: some View {
WithViewStore(self.store,
removeDuplicates: { _, _ in false }, // not sure how to handle it here without constraining state to Equatable
content: self.content)
}
} |
Beta Was this translation helpful? Give feedback.
-
OK, I decided to take up @stephencelis' challenge and try and implement something similar to what I suggested above. This is what I ended up with: SwitchStore(store) {
CaseLet(
state: /AppState.featureOne,
action: AppAction.featureOne,
then: FeatureView.init(store:)
)
CaseLet(
state: /AppState.featureTwo,
action: AppAction.featureTwo,
then: FeatureView.init(store:)
)
CaseLet(
state: /AppState.featureThree,
action: AppAction.featureThree,
then: FeatureView.init(store:)
)
} This is all powered by the new The downside to this implementation - due to the lack of variadic generics - is the need to define an overload for each number of cases you want to support. In my example code I've implemented up to three but I guess you could take this up to some reasonable maximum, just as SwiftUI does. Of course, each feature view needs to get a scoped store but you'll notice there's no reference to those scoped stores in the above example - to make this work I've leveraged SwiftUI's Because I needed an observable object that lets me get access to the parent store I have introduced a new class, similar to Internally the tuple views returned from the The full implementation along with some example usage can be found here: https://gist.github.com/lukeredpath/fde501070878ab3821d4fa41158df31f Let me know what you think! |
Beta Was this translation helpful? Give feedback.
-
Full circle ⭕️ https://www.pointfree.co/episodes/ep149-derived-behavior-optionals-and-enums#references |
Beta Was this translation helpful? Give feedback.
-
Just putting an idea out there, I'm not really sure what the best API for this would be.
TCA has good support for handling optional state and translating that to a conditional view with IfLetStore, but it feels like the equivalent story for enumerated state is missing, both in the domain and the view.
Right now, you can represent parts of your state as an enum, e.g. to represent distinct states that would also have distinct views but it's awkward to scope to them because the current scoping API is built around key paths.
You can overcome this with computed properties for each sub state, e.g.
With this boilerplate you can now conditionally show the view for each step by scoping over each sub step state using IfLetStore.
It would be great to able to scope based on case paths and lose the boilerplate and I do know some work on this has been done in a branch and I'm keen to give it a try.
However it would also be nice to see the enum equivalent of IfLetStore somehow. It seems a bit odd to have this enumerated state but then in the view there's no indication that the view for each state is distinct because IfLetStore only conveys optional state, not distinct steps.
Would it be possible to come up with some kind of API that allows you to specify the view for each case with the least amount of boilerplate? What would that view look like?
Beta Was this translation helpful? Give feedback.
All reactions