The simplest architecture for RxSwift
/**
Simulation of a discrete system (finite-state machine) with feedback loops.
Interpretations:
- [system with feedback loops](https://en.wikipedia.org/wiki/Control_theory)
- [fixpoint solver](https://en.wikipedia.org/wiki/Fixed_point)
- [local equilibrium point calculator](https://en.wikipedia.org/wiki/Mechanical_equilibrium)
- ....
System simulation will be started upon subscription and stopped after subscription is disposed.
System state is represented as a `State` parameter.
Events are represented by `Event` parameter.
- parameter initialState: Initial state of the system.
- parameter accumulator: Calculates new system state from existing state and a transition event (system integrator, reducer).
- parameter feedback: Feedback loops that produce events depending on current system state.
- returns: Current state of the system.
*/
public static func system<State, Event>(
initialState: State,
reduce: @escaping (State, Event) -> State,
feedback: (Observable<State>) -> Observable<Event>...
) -> Observable<State>
-
Simple
- If the system doesn't have state -> congrats, you have either a pure function or an observable sequence
- It the system does have state, here we are :)
- Interaction with that state is by definition a feedback loop.
- => It's just state + CQRS
-
Straightforward
- if it's state -> State
- if it's a way to modify state -> Event/Command
- it it's an effect -> encode it into part of state and then design a feedback loop
-
Declarative
- System behavior is first declaratively specified and effects begin after subscribe is called => Compile time proof there are no "unhandled states"
-
Debugging is easier
- A lot of logic is just normal pure function that can be debugged using Xcode debugger, or just printing the commands.
-
Can be applied on any level
- Entire system
- application (state is stored inside a database, CoreData, Firebase, Realm)
- view controller (state is stored inside
system
operator) - inside feedback loop (another
system
operator inside feedback loop)
-
Works awesome with dependency injection
-
Testing
- Reducer is a pure function, just call it and assert results
- In case effects are being tested -> TestScheduler
-
Can model circular dependencies
-
Completely separates business logic from effects (Rx).
- Business logic can be transpiled between platforms (ShiftJS, C++, J2ObjC)
Observable.system(
initialState: 0,
reduce: { (state, event) -> State in
switch event {
case .increment:
return state + 1
case .decrement:
return state - 1
}
},
scheduler: MainScheduler.instance,
feedback:
// UI is user feedback
UI.bind { state in
([
state.map(String.init).bind(to: label.rx.text)
], [
plus.rx.tap.map { Event.increment },
minus.rx.tap.map { Event.decrement }
])
}
)
Simple automatic feedback loop.
Observable.system(
initialState: State.humanHasIt,
reduce: { (state: State, event: Event) -> State in
switch event {
case .throwToMachine:
return .machineHasIt
case .throwToHuman:
return .humanHasIt
}
},
scheduler: MainScheduler.instance,
feedback:
// UI is human feedback
bindUI,
// NoUI, machine feedback
react(query: { $0.machinePitching }, effects: { () -> Observable<Event> in
return Observable<Int>
.timer(1.0, scheduler: MainScheduler.instance)
.map { _ in Event.throwToHuman }
})
)
Driver.system(
initialState: State.empty,
reduce: State.reduce,
feedback:
// UI, user feedback
bindUI,
// NoUI, automatic feedback
react(query: { $0.loadNextPage }, effects: { resource in
return URLSession.shared.loadRepositories(resource: resource)
.asDriver(onErrorJustReturn: .failure(.offline))
.map(Event.response)
})
)
Run RxFeedback.xcodeproj
> Example
to find out more.
- Elm - pretty close, feedback loops for effects instead of
Cmd
, which effects to perform are encoded into state and querried by feedback loops - Redux - kind of like this, but feedback loops instead of middleware
- Redux-Observable - observables observe state vs. being inside middleware between view and state
- Cycle.js - no simple explanation :), ask @andrestaltz
- MVVM - separates state from effects and doesn't require a view