Last active
May 15, 2020 14:23
-
-
Save danielt1263/0a71423c578f2bf3b15c to your computer and use it in GitHub Desktop.
Swift replacement for KVO
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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