Skip to content

Instantly share code, notes, and snippets.

@adam-zethraeus
Created December 20, 2023 23:41
Show Gist options
  • Save adam-zethraeus/2d7221e19bf204c813cfc93999ab076b to your computer and use it in GitHub Desktop.
Save adam-zethraeus/2d7221e19bf204c813cfc93999ab076b to your computer and use it in GitHub Desktop.
Slice binding
@_spi(Internals) import ComposableArchitecture
struct Bound<R: Reducer, S, V:View> {
init(store: StoreOf<R>, _ s: KeyPath<R.State, S>, _ a: @escaping (S) -> R.Action, @ViewBuilder _ vb: @escaping (Binding<S>) -> V) {
_state = Binding<S>{store.state.value[keyPath: s] } set:{ store.send(a($0)) }
self.vb = vb
}
@Binding var state: S
@ViewBuilder var vb: (Binding<S>) -> V
}
struct Slice<R: Reducer, S> {
internal init(_ select: KeyPath<R.State, S>, _ act: @escaping (S) -> R.Action) {
self.select = select
self.act = act
}
let select: KeyPath<R.State, S>
let act: (S) -> R.Action
func bind(from store: StoreOf<R>) -> Binding<S> {
.init(get: { store.state.value[keyPath: select]}, set: { store.send(act($0)) })
}
}
struct Slicer<R:Reducer, A, B, C> {
let a: Slice<R, A>
let b: Slice<R, B>
let c: Slice<R, C>
}
struct WithSlices<R: Reducer, A, B, C, V:View>: View {
init(
store: StoreOf<R>,
a: Slice<R, A>,
b: Slice<R, B>,
c: Slice<R, C>,
@ViewBuilder _ vb:@escaping (_ a: Binding<A>, _ b: Binding<B>, _ c: Binding<C>) -> V
) {
self.vb = vb
self.slicer = .init(a: a, b: b, c: c)
self.store = store
}
let store: StoreOf<R>
let slicer: Slicer<R, A, B, C>
let vb: (_ a: Binding<A>, _ b: Binding<B>, _ c: Binding<C>) -> V
var body: some View {
vb(slicer.a.bind(from: store), slicer.b.bind(from: store), slicer.c.bind(from: store))
}
}
extension Store {
func slice<RA: Reducer, A, B, C, V: View>(
a: Slice<RA, A>,
b: Slice<RA, B>,
c: Slice<RA, C>,
@ViewBuilder vb: @escaping (_ a: Binding<A>, _ b: Binding<B>, _ c: Binding<C>) -> V
) -> some View where State == RA.State, Action == RA.Action {
WithSlices<RA, A, B, C, V>(store: self, a: a, b: b, c: c, vb)
}
}
struct R: Reducer {
func reduce(into state: inout State, action: Action) -> ComposableArchitecture.Effect<Action> {
return .none
}
struct State: Equatable {
var a: Bool
}
enum Action {
case lol(Bool)
}
}
struct VB: View {
var x = StoreOf<R>(initialState: R.State(a: false), reducer: {R()})
var body: some View {
x.slice(a: Slice<R, Bool>(\.a, R.Action.lol), b: .init(\.a, R.Action.lol), c: .init(\.a, R.Action.lol)) { $a, $b, $c in
HStack {
Text("Yeah").opacity(a ? 1 : 0)
Text("Yeah").opacity(b ? 1 : 0)
Text("Yeah").opacity(c ? 1 : 0)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment