Created
November 13, 2017 02:32
-
-
Save mminer/410e9c57918cee0b191511ed3d5e8343 to your computer and use it in GitHub Desktop.
Example of a Redux-esque store powered by RxSwift.
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
typealias Reducer<StateType, ActionType> = (_ state: StateType, _ action: ActionType) -> StateType |
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 RxSwift | |
final class Store<StateType, ActionType> { | |
private(set) var state: StateType | |
private let reducer: Reducer<StateType, ActionType> | |
private let subject: BehaviorSubject<StateType> | |
init(reducer: @escaping Reducer<StateType, ActionType>, state: StateType) { | |
self.reducer = reducer | |
self.state = state | |
self.subject = BehaviorSubject(value: state) | |
} | |
deinit { | |
subject.onCompleted() | |
} | |
/// Dispatches an action to trigger a state change. | |
func dispatch(_ action: ActionType) { | |
state = reducer(state, action) | |
subject.onNext(state) | |
} | |
/// Subscribes to changes to the state. | |
func observe<T: Equatable>(_ keyPath: KeyPath<StateType, T>) -> Observable<T> { | |
return subject | |
.map { $0[keyPath: keyPath] } | |
.distinctUntilChanged() | |
} | |
/// Subscribes to changes to the state. | |
func observe<T: Equatable>(_ keyPath: KeyPath<StateType, [T]>) -> Observable<[T]> { | |
return subject | |
.map { $0[keyPath: keyPath] } | |
.distinctUntilChanged(==) | |
} | |
} |
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
enum Action { | |
case search(query: String) | |
case signIn(name: String, email: String) | |
case signOut | |
} | |
struct State { | |
let name: String | |
let email: String | |
let searchQuery: String | |
var isSignedIn: Bool { | |
return name.isEmpty && email.isEmpty | |
} | |
static let initial = State(name: "", email: "", searchQuery: "") | |
} | |
struct Reducers { | |
static func rootReducer(_ state: State, _ action: Action) -> State { | |
return State( | |
name: name(state, action), | |
email: email(state, action), | |
searchQuery: searchQuery(state, action) | |
) | |
} | |
private static func name(_ state: State, _ action: Action) -> String { | |
switch action { | |
case .signIn(let name) | |
return name | |
case .signOut: | |
return "" | |
default: | |
return state.name | |
} | |
} | |
private static func email(_ state: State, _ action: Action) -> String { | |
switch action { | |
case .signIn(_, let email) | |
return email | |
case .signOut: | |
return "" | |
default: | |
return state.email | |
} | |
} | |
private static func searchQuery(_ state: State, _ action: Action) -> String { | |
switch action { | |
case .search(let query) | |
return query | |
default: | |
return state.query | |
} | |
} | |
} | |
let store = Store(reducer: Reducers.root, state: .initial) | |
store.observe(\.name) | |
.subscribe(onNext: { name in | |
// Update UI | |
}) | |
store.observe(\.searchQuery) | |
.filter { !$0.isEmpty } | |
.debounce(300) | |
.flatMapLatest { searchQuery in | |
// Perform search | |
}) | |
.subscribe(onNext: { searchResults in | |
// Update UI | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment