Skip to content

Instantly share code, notes, and snippets.

@danielt1263
Last active May 15, 2020 14:23
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save danielt1263/0a71423c578f2bf3b15c to your computer and use it in GitHub Desktop.
Save danielt1263/0a71423c578f2bf3b15c to your computer and use it in GitHub Desktop.
Swift replacement for KVO
//
// Signal.swift
//
// Created by Daniel Tartaglia on 9/6/15.
// Copyright © 2016 Daniel Tartaglia. MIT License.
//
public protocol Disposable {
func dispose()
}
public class Signal<T> {
public typealias ValueType = T
public init() {
self.hasValue = false
self.value = nil
}
public init(value: ValueType) {
self.hasValue = true
self.value = value
}
/// All observers will be notified.
public func update(_ value: ValueType) {
self.hasValue = true
self.value = value
notify(value)
}
/// To observe changes in the signal, attach a block. When you want observation to end, call `dispose` on the returned Disposable.
public func attach(observer: @escaping (ValueType) -> Void) -> Disposable {
let wrapped = ObserverWrapper<T>(signal: self, function: observer)
observers.append(wrapped)
if let value = value, hasValue {
observer(value)
}
return wrapped
}
fileprivate func detach(_ wrappedObserver: ObserverWrapper<ValueType>) {
observers = observers.filter { $0 !== wrappedObserver }
}
private func notify(_ value: ValueType) {
observers.forEach { $0.update(value) }
}
private var hasValue: Bool // required for Signal<Void>
private var value: T?
private var observers: [ObserverWrapper<T>] = []
}
extension Signal {
public func map<U>(transform: @escaping (ValueType) -> U) -> Signal<U> {
let result = Signal<U>()
attach { value in
result.update(transform(value))
}
return result
}
public func filter(includeElement: @escaping (ValueType) -> Bool) -> Signal<ValueType> {
let result = Signal<T>()
attach { value in
if includeElement(value) {
result.update(value)
}
}
return result
}
public func flatMap<U>(transform: @escaping (ValueType) -> Signal<U>) -> Signal<U> {
let result = Signal<U>()
attach { value in
let signal = transform(value)
signal.attach { transformed in
result.update(transformed)
}
}
return result
}
}
private class ObserverWrapper<T>: Disposable {
init(signal: Signal<T>, function: @escaping (T) -> Void) {
self.signal = signal
self.function = function
}
func update(_ value: T) {
function(value)
}
func dispose() {
signal.detach(self)
}
unowned let signal: Signal<T>
let function: (T) -> Void
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment