Skip to content

Instantly share code, notes, and snippets.

@lorentey
Created October 21, 2016 18:54
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lorentey/a44b07380bc0a02abad6b6483ee9532f to your computer and use it in GitHub Desktop.
Save lorentey/a44b07380bc0a02abad6b6483ee9532f to your computer and use it in GitHub Desktop.
protocol Sink {
func receive()
}
protocol Source {
associatedtype Value
func subscribe(_ sink: Sink) -> Int
func unsubscribe(_ token: Int)
var valueInTransit: Value { get }
}
class Signal<Value>: Source {
private var _sinks: [Int: Sink] = [:]
private var _nextKey = 0
private var _value: Value? = nil
func subscribe(_ sink: Sink) -> Int {
let key = _nextKey
_sinks[key] = sink
_nextKey += 1
return key
}
var valueInTransit: Value {
return _value!
}
func unsubscribe(_ key: Int) {
_sinks[key] = nil
}
func send(_ value: Value) {
precondition(_value == nil)
_value = value
_sinks.forEach { _, sink in sink.receive() }
_value = nil
}
}
extension Source {
func subscribe(_ sink: @escaping (Value) -> Void) -> Int {
return subscribe(ClosureSink(source: self, sink: sink))
}
}
struct ClosureSink<S: Source>: Sink {
let source: S
let sink: (S.Value) -> Void
func receive() { sink(source.valueInTransit) }
}
class Foo {
func receive(_ value: String, with context: Int) {
print("\(value) from Foo with context \(context)")
}
}
struct FooSink<S: Source where S.Value == String>: Sink {
let source: S
let target: Foo
let context: Int
func receive() { target.receive(source.valueInTransit, 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 Sink's existential 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