Created
May 5, 2017 05:33
-
-
Save leojkwan/aa7d11a394db1c176b136d59bc680230 to your computer and use it in GitHub Desktop.
Native Observable Pattern in Swift
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 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() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thank you for this! ❤️