joels joels - 7 months ago 20
Swift Question

How to define a protocol with an array of a protocol with an associated type

I am trying to create a protocol (AppStore) which requires the conformer to implement an array of items (subscriptions) that conform to a protocol (Subscriber) that has an associated type.

goal
You can think of AppStore like an NSNotificationCenter. I want to add subscribers (like addObserver...). When something happens call handleNewState on the subscribers (like handleNotification:) and pass in an AppState conformer that actually has some variables set on it. The stock AppState doesn't have any properties available.

public protocol AppStore {
//trying to have an array of subscriptions in the protocol
//but a concrete Subscriber type needs to be specified, I thought a generic Subscriber would be more flexible here?
var subscriptions:[Subscription<>]{get set}// Reference to generic type 'Subscription' requires arguments in <...>

associatedtype AppStateType:AppState
var appState:AppStateType { get set }
}
extension AppStore {
//so that AppStore can implement this function
public mutating func subscribe<T:protocol<Subscriber, AnyObject>>(aSubscriber:T){
subscriptions.append(Subscription(sub:aSubscriber))
}
}

public protocol Subscriber {
associatedtype AppStateType
func handleNewState(newState:AppStateType)
}

public struct Subscription <T:protocol<Subscriber, AnyObject>> {
private weak var subscriber:T? = nil
init(sub:T){
self.subscriber = sub
}
}
public protocol AppState { }


How am I supposed to define
var subscriptions:[Subscription<>]?
Or should I be doing this a different way

I would use it like this

public struct OSXAppState:AppState {
var someStateProp:Int
}
extension NSView : Subscriber {
public func handleNewState(newState:OSXAppState){
if newState == 1 { //do this }
else { //do that }
}
}
public struct OSXAppStore : AppStore {
public typealias GenericSubscriber = NSView//???: something more generic
public var subscriptions = [Subscription<GenericSubscriber>]()
public var appState: AppState//???: would like to specify OSXAppState
}

Answer

I assume that you were adhering to AnyObject in order to be able to use weak. This can be done more simply by making the protocol class-only. I changed the protocol composition

protocol<Subscriber, AnyObject>

to

protocol Subscriber: class

I also added

associatedtype GenericSubscriber: SubscriberType

to allow the generic

Subscription<T: SubscriberType>

to be used in the array.

public protocol Subscriber: class {
  associatedtype AppStateType:AppState
  func handleNewState(newState:AppStateType)
}

public struct Subscription<T:Subscriber> {
  private weak var subscriber:T?
  init(sub:T){
    self.subscriber = sub
  }
}

public protocol AppState { }

public protocol AppStore {
  // Allows Subscription<T:Subscriber> to be used as an array element
  associatedtype GenericSubscriber:Subscriber

  var subscriptions:[Subscription<GenericSubscriber>]{get set}
  var appState:AppState { get set }
}

extension AppStore {
  // The concrete type of GenericSubscriber is inferred from this context
  public mutating func subscribe(aSubscriber: GenericSubscriber){
    subscriptions.append(Subscription<GenericSubscriber>(sub:aSubscriber))
  }
}

public struct OSXAppState:AppState {
  var someStateProp:Int
}
extension NSView : Subscriber {
  public func handleNewState(newState:OSXAppState){
  }
}
Comments