Last active
February 28, 2018 05:56
-
-
Save nferruzzi/a36e2be5c5da7dbe25e90a56fd1049ad to your computer and use it in GitHub Desktop.
My own ReSwift multi observer extension with support for equatable types and optionals of equatable types
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
// Promise like subscriber, works with Equatable types and Optionals of Equatable types | |
// ie: | |
// let observer = document.mainStore | |
// .select { (state) -> UIState in state.ui } | |
// .then { _ in called += 1 } | |
// or | |
// let observer = document.mainStore | |
// .select { (state) -> String? in state.ui.selection } | |
// .then { _ in called += 1 } | |
// | |
// To unsubscribe: | |
// observer.unsubscribe() when needed | |
// | |
// By [email protected] Use at your own risk :) | |
extension Store { | |
final class SubscriberBoxed<T: Equatable>: StoreSubscriber { | |
private var execute: ((T) -> Void)? | |
private var subscriber: (() -> Void)? | |
private var unsubscriber: (() -> Void)? | |
func then(execute: @escaping (T) -> Void) -> Self { | |
self.execute = execute | |
if let subscriber = subscriber { subscriber() } | |
subscriber = nil | |
return self | |
} | |
func unsubscribe() { | |
if let unsubscriber = unsubscriber { unsubscriber() } | |
unsubscriber = nil | |
} | |
internal func newState(state: T) { | |
guard let execute = execute else { return } | |
execute(state) | |
} | |
init(store: Store<State>, selector: @escaping (State) -> T) { | |
subscriber = { [weak self, weak store] in | |
guard let wself = self, let wstore = store else { return } | |
wstore.subscribe(wself) { subscription in | |
subscription | |
.select { (state: State) -> T in selector(state) } | |
.skipRepeats { (old: T, new: T) -> Bool in | |
return old == new | |
} | |
} | |
} | |
unsubscriber = { [weak self, weak store] in | |
guard let wself = self, let wstore = store else { return } | |
wstore.unsubscribe(wself) | |
} | |
} | |
deinit { | |
unsubscribe() | |
} | |
} | |
final class SubscriberBoxedOptional<T: Equatable>: StoreSubscriber { | |
private var execute: ((T?) -> Void)? | |
private var subscriber: (() -> Void)? | |
private var unsubscriber: (() -> Void)? | |
func then(execute: @escaping (T?) -> Void) -> Self { | |
self.execute = execute | |
if let subscriber = subscriber { subscriber() } | |
subscriber = nil | |
return self | |
} | |
func unsubscribe() { | |
if let unsubscriber = unsubscriber { unsubscriber() } | |
unsubscriber = nil | |
} | |
internal func newState(state: T?) { | |
guard let execute = execute else { return } | |
execute(state) | |
} | |
init(store: Store<State>, selector: @escaping (State) -> T?) { | |
subscriber = { [weak self, weak store] in | |
guard let wself = self, let wstore = store else { return } | |
wstore.subscribe(wself) { subscription in | |
subscription | |
.select { (state: State) -> T? in | |
return selector(state) | |
} | |
.skipRepeats { (old: T?, new: T?) -> Bool in | |
switch (old, new) { | |
case (nil, nil): | |
return true | |
case (_, nil): | |
return false | |
case (nil, _): | |
return false | |
case let (new, old): | |
return old == new | |
} | |
} | |
} | |
} | |
unsubscriber = { [weak self, weak store] in | |
guard let wself = self, let wstore = store else { return } | |
wstore.unsubscribe(wself) | |
} | |
} | |
deinit { | |
unsubscribe() | |
} | |
} | |
func select<T: Equatable>(selector: @escaping (State) -> T) -> SubscriberBoxed<T> { | |
return SubscriberBoxed(store: self, selector: selector) | |
} | |
func select<T: Equatable>(selector: @escaping (State) -> T?) -> SubscriberBoxedOptional<T> { | |
return SubscriberBoxedOptional(store: self, selector: selector) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment