Skip to content

Instantly share code, notes, and snippets.

@nferruzzi
Last active February 28, 2018 05:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save nferruzzi/a36e2be5c5da7dbe25e90a56fd1049ad to your computer and use it in GitHub Desktop.
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
// 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 nicola.ferruzzi@gmail.com 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