Skip to content

The universal system operator and architecture for RxSwift

License

Notifications You must be signed in to change notification settings

NoTests/RxFeedback.swift

Repository files navigation

RxFeedback

Travis CIplatformspodCarthage compatibleSwift Package Manager compatible

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>

Why

  • 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 insidesystemoperator)
    • inside feedback loop (anothersystemoperator 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)

Examples

Simple UI Feedback loop

Complete example

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
)
}
)

Play Catch

Simple automatic feedback loop.

Complete example

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}
})
)

Paging

Complete example

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>Exampleto find out more.

Installation

CocoaPods

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

Carthage

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 updateto build the framework and drag the builtRxFeedback.frameworkinto your Xcode project. AsRxFeedbackdepends onRxSwiftandRxCocoayou need to drag theRxSwift.frameworkandRxCocoa.frameworkinto your Xcode project as well.

Swift Package Manager

TheSwift Package Manageris a tool for automating the distribution of Swift code and is integrated into theswiftcompiler.

Once you have your Swift package set up, adding RxFeedback as a dependency is as easy as adding it to thedependenciesvalue of yourPackage.swift.

dependencies:[
.package(url:"https://github /NoTests/RxFeedback.swift.git",majorVersion:1)
]

Difference from other architectures

  • Elm - pretty close, feedback loops for effects instead ofCmd,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