Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save lorentey/09eccfaa34fd75a4b2d0ff6230185fa1 to your computer and use it in GitHub Desktop.
Save lorentey/09eccfaa34fd75a4b2d0ff6230185fa1 to your computer and use it in GitHub Desktop.
protocol Sink {
associatedtype Value
func receive(_ value: Value)
}
// This assumes Generalized Existentials are a thing
// https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#generalized-existentials
typealias AnySink<Value> = Any<Sink where .Value == Value>
protocol Source {
associatedtype Value
func subscribe(_ sink: Sink) -> Int
func unsubscribe(_ token: Int)
}
class Signal<Value>: Source {
private var _sinks: [Int: AnySink<Value>] = [:]
private var _nextKey = 0
func subscribe(_ sink: Sink) -> Int {
let key = _nextKey
_sinks[key] = sink
_nextKey += 1
return key
}
func unsubscribe(_ key: Int) {
_sinks[key] = nil
}
func send(_ value: Value) {
_sinks.forEach { _, sink in sink.receive(value) }
}
}
extension Source {
func subscribe(_ sink: @escaping (Value) -> Void) -> Int {
return subscribe(ClosureSink(source: self, sink: sink))
}
}
struct ClosureSink<Value>: Sink {
let sink: (Value) -> Void
func receive(_ value: Value) {
sink(value)
}
}
class Foo {
func receive(_ value: String, with context: Int) {
print("\(value) from Foo with context \(context)")
}
}
struct FooSink: Sink {
let target: Foo
let context: Int
func receive(_ value: String) { target.receive(value, with: context) }
}
let foo = Foo()
let signal = Signal<String>()
let k1 = signal.subscribe { print("\($0) from closure observer") }
// Note that FooSink fits in 3 words, so AnySink probably won't allocate a box for it.
let k2 = signal.subscribe(FooSink(source: signal, target: foo, context: 42))
signal.send("Hello")
// => Hello from closure observer
// => Hello from Foo with context 42
signal.unsubscribe(k1)
signal.unsubscribe(k2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment