Skip to content

Instantly share code, notes, and snippets.

@xaphod
Created May 14, 2020 00:49
Show Gist options
  • Save xaphod/4f8a6402429759b6b3fd8ea2d8ea53c4 to your computer and use it in GitHub Desktop.
Save xaphod/4f8a6402429759b6b3fd8ea2d8ea53c4 to your computer and use it in GitHub Desktop.
Using associatedtype in protocols with type erasure to have two protocols restricted to the same type
import Foundation
// SINGLE VARIANT - Observer only has one thing it is watching
protocol IsObserver: class {
associatedtype DataType
func dataDidUpdate(_ data: [DataType])
}
protocol HasObserver {
associatedtype DataType
static var observer: AnyHasObserver<DataType> { get }
}
class AnyHasObserver<T> {
typealias ObserverDataBlock = ([T])->Bool
var observerDataBlocks = [ObjectIdentifier:ObserverDataBlock]()
func addObserver<L: IsObserver>(_ observer: L) where L.DataType == T {
let id = ObjectIdentifier(observer)
self.observerDataBlocks[id] = { [weak observer] (data) in
guard let observer = observer else { return false }
observer.dataDidUpdate(data)
return true
}
}
func removeObserver<L: IsObserver>(_ observer: L) where L.DataType == T {
let id = ObjectIdentifier(observer)
self.observerDataBlocks.removeValue(forKey: id)
}
func tellObserversDataDidUpdate(_ data: [T]) {
WLog("HasObserver(\(T.self)) tellObserversDataDidUpdate - \(data.count) new values")
let keysToRemove = self.observerDataBlocks.compactMap { (key: ObjectIdentifier, value: ObserverDataBlock)->ObjectIdentifier? in
if value(data) { return nil }
return key
}
keysToRemove.forEach { self.observerDataBlocks.removeValue(forKey: $0) }
if keysToRemove.count > 0 {
WLog("HasObserver(\(T.self)) tellObserversDataDidUpdate: removed \(keysToRemove.count) dead blocks")
}
}
}
// MULTI VARIANT - Observer watching multiple types
protocol IsObserverOfMultipleTypes: class {
func dataDidUpdate(_ data: [Any], type: Any.Type)
}
protocol HasObserverOfMultipleTypes {
static var observer: AnyHasObserverOfMultipleTypes { get }
}
class AnyHasObserverOfMultipleTypes {
typealias ObserverDataBlock = ([Any], Any.Type)->Bool
var observerDataBlocks = [ObjectIdentifier:ObserverDataBlock]()
func addObserver(_ observer: IsObserverOfMultipleTypes) {
let id = ObjectIdentifier(observer)
self.observerDataBlocks[id] = { [weak observer] (data, type) in
guard let observer = observer else { return false }
observer.dataDidUpdate(data, type: type)
return true
}
}
func removeObserver(_ observer: IsObserverOfMultipleTypes) {
let id = ObjectIdentifier(observer)
self.observerDataBlocks.removeValue(forKey: id)
}
func tellObserversDataDidUpdate(_ data: [Any], type: Any.Type) {
WLog("HasObserverOfMultipleTypes(\(type)) tellObserversDataDidUpdate - \(data.count) new values")
let keysToRemove = self.observerDataBlocks.compactMap { (key: ObjectIdentifier, value: ObserverDataBlock)->ObjectIdentifier? in
if value(data, type) { return nil }
return key
}
keysToRemove.forEach { self.observerDataBlocks.removeValue(forKey: $0) }
if keysToRemove.count > 0 {
WLog("HasObserverOfMultipleTypes(\(data.self)) tellObserversDataDidUpdate: removed \(keysToRemove.count) dead blocks")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment