Add object to an array that confirm to the protocol that has associated type in it

59 Views Asked by At

I have a problem to write the code that puts objects into the observers array. The objects that are problematic implement the Observer protocol. Here is the code that shows what I want to do:

protocol Observer {
    associatedtype ValueType
    func update(value: ValueType)
}

struct Subject<T> {
    private var observers = Array<Observer>()

    mutating func attach(observer: Observer) {
        observers.append(observer)
    }

    func notyfi(value: T) {
        for observer in observers {
            observer.update(value: value)
        }
    }
}
2

There are 2 best solutions below

0
On BEST ANSWER

If your deployment target is at least a late 2022 release (iOS 16, macOS 13, etc.), you can use a constrained existential:

protocol Observer<ValueType> {
    associatedtype ValueType
    func update(value: ValueType)
}

struct Subject<T> {
    private var observers = Array<any Observer<T>>()

    mutating func attach(observer: any Observer<T>) {
        observers.append(observer)
    }

    func notify(value: T) {
        for observer in observers {
            observer.update(value: value)
        }
    }
}

If your deployment target is earlier, the Swift runtime doesn't support constrained existentials. (From SE-0353: “It is worth noting that this feature requires revisions to the Swift runtime and ABI that are not backwards-compatible nor backwards-deployable to existing OS releases.”) But you can use closures instead:

protocol Observer<ValueType> {
    associatedtype ValueType
    func update(value: ValueType)
}

struct Subject<T> {
    private var observers: [(T) -> Void] = []
    
    mutating func attach<O: Observer>(observer: O)
    where O.ValueType == T
    {
        observers.append { observer.update(value: $0) }
    }

    func notify(value: T) {
        for observer in observers {
            observer(value)
        }
    }
}
1
On

Why don't you use the Observer as the generic parameter?

struct Subject<O: Observer> {

    typealias T = O.ValueType

    private var observers = Array<O>()

    mutating func attach(observer: O) {
        observers.append(observer)
    }

    func notyfi(value: T) {
        for observer in observers {
            observer.update(value: value)
        }
    }
}