We can use Store and compose a new ViewStore to binding a smaller UI Block in subView or subViewController. How does TCA implements in binding each other ?
I crate some code to reproduce it.
import Foundation | |
import Combine | |
class Store<State, Action> { | |
var publisher: CurrentValueSubject<State, Never> | |
var void: Reducer<State, Action> | |
var cancelable: AnyCancellable? | |
init(state: State, reducer: @escaping Reducer<State, Action>) { | |
publisher = CurrentValueSubject(state) | |
self.void = reducer | |
} | |
deinit { | |
print("\(Self.self) is deinit") | |
} | |
func send(action: Action) { | |
void(&publisher.value, action) | |
} | |
func viewStore()-> ViewStore<State, Action> { | |
let child = ViewStore(value: publisher.value, root: { | |
[weak self] action in | |
self?.send(action: action) | |
}) | |
child.cancelable = publisher | |
.dropFirst() | |
.sink(receiveValue: { [weak child] in | |
child?.value = $0 | |
}) | |
return child | |
} | |
} | |
class ViewStore<State, Action> { | |
var cancelable: AnyCancellable? | |
var publisher: CurrentValueSubject<State, Never> | |
var root: ((Action)-> Void)? | |
init(value: State, root: @escaping (Action)-> Void) { | |
self.publisher = CurrentValueSubject(value) | |
self.root = root | |
} | |
func send(action: Action) { | |
root?(action) | |
} | |
deinit { | |
print("\(Self.self) is deinit") | |
} | |
} | |
extension ViewStore { | |
var value: State { | |
get { | |
self.publisher.value | |
} | |
set { | |
self.publisher.send(newValue) | |
} | |
} | |
} | |
import Foundation | |
import Combine | |
enum CounterAction { | |
case decrement | |
case increment | |
} | |
struct CounterState { | |
var count: Int = 0 | |
} | |
typealias Reducer<State, Action> = (inout State, Action)-> Void | |
let reducer: Reducer<CounterState, CounterAction> = { state , action in | |
switch action { | |
case .decrement: | |
state.count -= 1 | |
case .increment: | |
state.count += 1 | |
} | |
} | |
var bag: Set<AnyCancellable> = [] | |
var parent: Store! = Store(state: CounterState(), reducer: reducer) | |
var child: ViewStore! = parent.viewStore() | |
child.publisher.print().sink(receiveValue: {_ in }).store(in: &bag) | |
child.send(action: .decrement) // -1 | |
child.send(action: .increment) // 0 | |
child.send(action: .decrement) // -1 | |
child.send(action: .decrement) // -2 | |
child.send(action: .decrement) // -3 | |
print(parent.publisher.value.count == -3) | |
print(child.value.count == -3) |