Reputation:
I am writing a library for state management. It's basically a simplified observer pattern, with just 1 single observer/listener.
Right now I have this and it worked well:
public final class StateStore<S> {
private var currentState: S
public var listener: ((S) -> Void)? = nil
public init(initialState: S) {
currentState = initialState
}
func update(_ block: (inout S) -> Void) {
var nextState = currentState // struct's copy on write
block(&nextState)
currentState = nextState
listener?(currentState)
}
}
However, I would like to change it to protocol instead of block. Something like:
public protocol StateListener: AnyObject {
associatedtype S
func didUpdateState(_ state: S)
}
public final class StateStore<S> {
...
public weak var listener: StateListener<S>? // <- how to deal with associate type
...
}
I am not able to do so because in the above S
is associate type, not generic type. So I got error saying Cannot specialize non-generic type 'StateListener'
I have looked at this but not helpful: Using generic protocols in generic classes
Upvotes: 0
Views: 867
Reputation: 274885
The key is to parameterise StateStore
with a Listener
type, rather than an S
type. S
can then be defined as a type alias of Listener.S
:
public final class StateStore<Listener: StateListener> {
public typealias S = Listener.S
Notice that now you can't have an variable of type StateStore<StateListener>
s, that can store all kinds of StateStore
s with different implementations of StateListener
s. The type StateStore<StateListener>
doesn't really make sense, and for good reason too.
I would say that wanting to make this into a protocol is rather weird. If you want to give the (S) -> Void
type a name, use a type alias inside StateStore
:
public typealias StateListener = ((S) -> Void)
Upvotes: 0
Reputation:
S is not the Swift style; using full words is the modern standard.
public protocol StateListener: AnyObject {
associatedtype State
func didUpdateState(_: State)
}
public final class StateStore<Listener: StateListener> {
public weak var listener: Listener?
}
You don't need to manually account for the associated type. It's built in, accessible as a nested type:
func ƒ(_: Listener.State) {
}
Upvotes: 1