The simplest architecture forRxSwift
typealiasFeedback<State,Event>=(Observable<State>)->Observable<Event>
publicstaticfuncsystem<State,Event>(
initialState:State,
reduce:@escaping(State,Event)->State,
feedback:Feedback<State,Event>...
)->Observable<State>
-
Straightforward
- If it did happen -> Event
- If it should happen -> Request
- To fulfill Request -> 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)->Statein
switch event{
case.increment:
returnstate+1
case.decrement:
returnstate-1
}
},
scheduler:MainScheduler.instance,
feedback:
// UI is user feedback
bind(self){me,state->Bindings<Event>in
letsubscriptions=[
state.map(String.init).bind(to:me.label.rx.text)
]
letevents=[
me.plus.rx.tap.map{Event.increment},
me.minus.rx.tap.map{Event.decrement}
]
returnBindings(
subscriptions:subscriptions,
events:events
)
}
)
Simple automatic feedback loop.
Observable.system(
initialState:State.humanHasIt,
reduce:{(state:State,event:Event)->Statein
switch event{
case.throwToMachine:
return.machineHasIt
case.throwToHuman:
return.humanHasIt
}
},
scheduler:MainScheduler.instance,
feedback:
// UI is human feedback
bindUI,
// NoUI, machine feedback
react(request:{$0.machinePitching},effects:{(_)->Observable<Event>in
returnObservable<Int>
.timer(1.0,scheduler:MainScheduler.instance)
.map{_inEvent.throwToHuman}
})
)
Driver.system(
initialState:State.empty,
reduce:State.reduce,
feedback:
// UI, user feedback
bindUI,
// NoUI, automatic feedback
react(request:{$0.loadNextPage},effects:{resourcein
returnURLSession.shared.loadRepositories(resource:resource)
.asSignal(onErrorJustReturn:.failure(.offline))
.map(Event.response)
})
)
RunRxFeedback.xcodeproj
>Example
to find out more.
CocoaPodsis a dependency manager for Cocoa projects. You can install it with the following command:
$ gem install cocoapods
To integrate RxFeedback into your Xcode project using CocoaPods, specify it in yourPodfile
:
pod'RxFeedback','~> 3.0'
Then, run the following command:
$ pod install
Carthageis a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.
You can install Carthage withHomebrewusing the following command:
$ brew update
$ brew install carthage
To integrate RxFeedback into your Xcode project using Carthage, specify it in yourCartfile
:
github "NoTests/RxFeedback" ~> 3.0
Runcarthage update
to build the framework and drag the builtRxFeedback.framework
into your Xcode project. AsRxFeedback
depends onRxSwift
andRxCocoa
you need to drag theRxSwift.framework
andRxCocoa.framework
into your Xcode project as well.
TheSwift Package Manageris a tool for automating the distribution of Swift code and is integrated into theswift
compiler.
Once you have your Swift package set up, adding RxFeedback as a dependency is as easy as adding it to thedependencies
value of yourPackage.swift
.
dependencies:[
.package(url:"https://github /NoTests/RxFeedback.swift.git",majorVersion:1)
]
- Elm - pretty close, feedback loops for effects instead of
Cmd
,which effects to perform are encoded into state and queried 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