Last active
May 17, 2017 08:04
-
-
Save rcharlton/ac6f8119d137e761f21dc595376df7a2 to your computer and use it in GitHub Desktop.
A super-simple Observer pattern.
This file contains 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
import Foundation | |
public final class Observer<T> { | |
public weak var subject: Observable<T>? | |
fileprivate var identifier: UInt | |
fileprivate var isValid: Bool { | |
return subject != nil | |
} | |
fileprivate func invalidate() { | |
subject = nil | |
} | |
fileprivate init(subject: Observable<T>, identifier: UInt) { | |
self.subject = subject | |
self.identifier = identifier | |
} | |
deinit { | |
stopObserving() | |
} | |
public func stopObserving() { | |
subject?.removeObserver(self) | |
} | |
} | |
/** | |
A super-simple value observer. | |
Not thread-safe. | |
*/ | |
public final class Observable<T> { | |
public typealias Closure = (T, T) -> Void | |
/// The underlying value, which is observed. | |
public var value: T { | |
didSet { | |
observers.forEach { (_, closure) in | |
closure(oldValue, value) | |
} | |
} | |
} | |
private var observers = [UInt: Closure]() | |
/** | |
Initialise this observable with a value. | |
*/ | |
public init(_ value: T) { | |
self.value = value | |
} | |
/** | |
Observe the stored value using the given closure. | |
The given closure is retained until a corresponding call | |
to removeObserver is made. | |
- parameter closure: Invoked with the old and new values | |
when a change occurs. | |
- returns: A value identifying the closure. | |
*/ | |
public func observe(using closure: @escaping Closure) -> Observer<T> { | |
let identifier = Global.counter.value | |
Global.counter.increment() | |
observers[identifier] = closure | |
return Observer<T>(subject: self, identifier: identifier) | |
} | |
/** | |
Removes a previously subscribed observer. | |
*/ | |
fileprivate func removeObserver(_ observer: Observer<T>) { | |
guard observer.isValid else { return } | |
observers.removeValue(forKey: observer.identifier) | |
observer.invalidate() | |
} | |
} | |
postfix operator • | |
/** | |
A shortcut for the value property. | |
- returns: the stored value of an Observable. | |
*/ | |
public postfix func •<T>(a: Observable<T>) -> T { | |
return a.value | |
} | |
infix operator ==> | |
/** | |
A shortcut for the observe(using:) method. | |
*/ | |
@discardableResult public func ==><T>( | |
subject: Observable<T>, | |
closure: @escaping Observable<T>.Closure) | |
-> Observer<T> { | |
return subject.observe(using: closure) | |
} | |
private enum Global { | |
static var counter = Counter() | |
} | |
/** | |
Basic integer counter; rolls over at MAX_UINT. | |
*/ | |
private struct Counter { | |
private(set) var value: UInt = 0 | |
mutating func increment() { | |
value += 1 | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment