Skip to content

Instantly share code, notes, and snippets.

@mansbernhardt
Last active June 5, 2018 05:15
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 mansbernhardt/724e9a11e6f58a0ff1f279b0afb62d95 to your computer and use it in GitHub Desktop.
Save mansbernhardt/724e9a11e6f58a0ff1f279b0afb62d95 to your computer and use it in GitHub Desktop.
API Design - Deriving Signal Playground
//: Playground - noun: a place where people can play
import Foundation
protocol Disposable {
func dispose()
}
final class Disposer: Disposable {
private var disposer: (() -> Void)?
init(_ disposer: @escaping () -> Void) {
self.disposer = disposer
}
func dispose() {
disposer?()
disposer = nil // Stop from being called more than once
}
deinit { dispose() }
}
final class DisposeBag: Disposable {
fileprivate var disposables: [Disposable] = []
func dispose() {
disposables.forEach { $0.dispose() }
disposables = []
}
deinit { dispose() }
}
func +=(bag: DisposeBag, disposable: Disposable) {
bag.disposables.append(disposable)
}
typealias Key = UInt64
private var _nextKey: Key = 0
func generateKey() -> Key {
_nextKey = _nextKey &+ 1
return _nextKey
}
final class Callbacker<Value> {
private var callbacks: [Key : (Value) -> ()] = [:]
func addCallback(_ callback: @escaping (Value) -> Void) -> Disposable {
let key = generateKey()
callbacks[key] = callback
return Disposer {
self.callbacks[key] = nil
}
}
public func callAll(_ value: Value) {
callbacks.forEach { $1(value) }
}
}
final class Signal<Value> {
let onValue: (@escaping (Value) -> ()) -> Disposable
init(onValue: @escaping (@escaping (Value) -> ()) -> Disposable) {
self.onValue = onValue
}
}
extension Signal {
func map<T>(transform: @escaping (Value) -> T) -> Signal<T> {
return Signal<T> { callback in
self.onValue { event in
callback(transform(event))
}
}
}
}
let bag = DisposeBag()
let passwordCallbacker = Callbacker<String>()
let passwordSignal = Signal(onValue: passwordCallbacker.addCallback)
let enableButtonSignal = passwordSignal.map { $0.count > 5 }
bag += enableButtonSignal.onValue {
print("Button enabled", $0)
}
passwordCallbacker.callAll("")
passwordCallbacker.callAll("1234")
passwordCallbacker.callAll("123456")
extension NotificationCenter {
func signal(forName name: Notification.Name?, object: Any? = nil) -> Signal<Notification> {
return Signal { completion in
let observer = self.addObserver(forName: name, object: object, queue: nil, using: completion)
return Disposer {
self.removeObserver(observer)
}
}
}
}
extension Notification.Name {
static let somethingHappend = Notification.Name(rawValue: "somethingHappend")
}
bag += NotificationCenter.default.signal(forName: .somethingHappend).onValue { _ in
print("Notification somethingHappend")
}
NotificationCenter.default.post(name: .somethingHappend, object: nil)
print("Got here")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment