I am developing a state management library. The original design only has 1 listener, which works great until I need to support multiple listeners.
The original design is here: Swift how to use generic protocol in generic class
This is what I have done to support multiple listeners:
public protocol StateObserver: AnyObject {
associatedtype State
func didUpdateState(_ state: State)
}
public final class StateStore<Observer: StateObserver> {
struct WeakRef<T: AnyObject> {
weak var value: T?
}
public private(set) var state: Observer.State
private var observers = [WeakRef<Observer>]()
public init(initialState: Observer.State) {
state = initialState
}
public func addObservers(_ observers: [Observer]) {
self.observers += observers.map { WeakRef(value: $0) }
}
public func update(_ block: (inout Observer.State) -> Void) {
var nextState = state
block(&nextState)
state = nextState
notify()
}
public func notify() {
for observer in observers {
observer.value?.didUpdateState(state)
}
}
}
Now I need to create the store with 2 observers:
class MyScene: SKScene {
init {
let leftPanel = LeftPanelSKNode()
let topBar = TopBarSKNode()
let store: StateStore<?> // How to make this support `LeftPanelSKNode `, `TopBarSKNode`, and `MyScene`?
store.addObservers([leftPanel, topBar, self])
}
Now I am stuck here. I need to create a StateStore<?> of something, which can be either MyScene
, LeftPanelSKNode
and TopBarSKNode
.
First of all, I have to say that what you are building already exists in many reactive libraries:
CurrentValueSubject in Apple's Combine;
BehaviorSubject in RxSwift;
You can also check the small internal class I've made myself, it allows to hold the state and observe it ObservableProperty .
Back to your question, I've found a way to add the StateObserver one by one while keeping only the weak reference to them.
public protocol StateObserver: AnyObject {
associatedtype State
func didUpdateState(_ state: State)
}
class Node1: StateObserver {
typealias State = Int
func didUpdateState(_ state: Int) { }
}
class Node2: StateObserver {
typealias State = Int
func didUpdateState(_ state: Int) { }
}
class StateStore<StateType> {
private(set) var state: StateType
init(_ initialState: StateType) {
self.state = initialState
}
private var observers: [(StateType) -> Void] = []
func observe<Observer: StateObserver>(by observer: Observer) where Observer.State == StateType {
weak var weakObserver = observer
observers.append { state in
weakObserver?.didUpdateState(state)
}
}
func notify() {
observers.forEach {
$0(self.state)
}
}
}
let store = StateStore<Int>(0)
let node1 = Node1()
let node2 = Node2()
store.observe(by: node1)
store.observe(by: node2)
Adding the array-based observe API might be a problem because of the associatedtype in the StateObserver.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.