Skip to content

Instantly share code, notes, and snippets.

@leojkwan
Created May 5, 2017 05:33
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save leojkwan/aa7d11a394db1c176b136d59bc680230 to your computer and use it in GitHub Desktop.
Save leojkwan/aa7d11a394db1c176b136d59bc680230 to your computer and use it in GitHub Desktop.
Native Observable Pattern in Swift
import UIKit
class PushedViewController: UIViewController {
var currentWeather: Observable<Double>!
private let disposeBag = MyDisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
// Add an observer everytime view controller is presented
currentWeather.observe { newTemperature in
print("lastest temperature #\(newTemperature)")
}.disposed(by: disposeBag)
}
deinit {
print("deallocating pushed view controller")
}
}
class ViewController: UIViewController {
private let disposeBag = MyDisposeBag()
var currentWeather: Observable<Double> = Observable(80)
@IBOutlet weak var observerCountLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
currentWeather.observe { [weak self] newTemperature in
guard let strongSelf = self else { return }
strongSelf.observerCountLabel.text = String(describing: strongSelf.currentWeather.observers.count)
}.disposed(by: disposeBag)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
currentWeather.value = 100
}
@IBAction func pushing(_ sender: Any) {
let dvc = storyboard?.instantiateViewController(withIdentifier: "PushedViewController") as! PushedViewController
dvc.currentWeather = currentWeather
navigationController?.pushViewController(dvc, animated: true)
}
}
class Observable<T> {
typealias Observer = (T) -> Void
private(set) var observers = [Int: Observer]()
func observe(_ observer: @escaping Observer)-> Disposable {
let uniqueKey = Int(arc4random_uniform(10000))
// add observer to observers
observers[uniqueKey] = (observer)
print("total observer count: \(observers.keys.count)")
return ObserverDisposable(owner: self, key: uniqueKey)
}
func updateObservers() {
for (_, observer) in observers {
// iterate over all observers,
// and call closure with new value.
observer(value)
}
}
var value: T {
didSet {
updateObservers()
}
}
func removeObserver(with key: Int) {
if observers.keys.contains(key) {
observers.removeValue(forKey: key)
updateObservers()
}
}
init(_ v: T) {
value = v
}
}
protocol Disposable {
func dispose()
}
extension Disposable {
func disposed(by bag: MyDisposeBag) {
bag.add(self)
}
}
class ObserverDisposable<T>: Disposable {
var key: Int
weak var owner: Observable<T>?
init(owner: Observable<T>, key: Int) {
self.owner = owner
self.key = key
}
func dispose() {
self.owner?.removeObserver(with: key)
}
}
class MyDisposeBag {
var disposables: [Disposable] = []
func add(_ disposable: Disposable) {
disposables.append(disposable)
print("new dispose bag count: \(disposables.count)")
}
func dispose() {
disposables.forEach({$0.dispose()})
}
// When our view controller deinits, our dispose bag will deinit as well
// and trigger the disposal of all corresponding observers living in the
// Observable, which Disposable has a weak reference to: 'owner'.
deinit {
dispose()
}
}
@manmal
Copy link

manmal commented Dec 25, 2018

Thank you for this! ❤️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment