简体   繁体   中英

Swift how to conform to different associate type in a protocol

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM