RxSwiftCommunity/RxState

Name: RxState

Owner: RxSwift Community

Description: Redux implementation in Swift using RxSwift

Created: 2017-05-12 07:37:59.0

Updated: 2018-05-18 20:50:47.0

Pushed: 2017-10-22 21:43:38.0

Homepage:

Size: 523

Language: Swift

GitHub Committers

UserMost Recent Commit# Commits

Other Committers

UserEmailMost Recent Commit# Commits

README

RxState RxState: Redux + RxSwift

RxState a predictable state container for Swift apps. It's a tiny library built on top of RxSwift and inspired by Redux that facilitates building Unidirectional Data Flow architecture.

Why Unidirectional Data Flow Architecture?
  1. Helps you manage state in a consistent and unified way that guaranty it?s always predictable (After all, state is the source of all evil and you wanna keep that evil in check).
  2. Limits the way app state can be mutated, which makes your app easier to understand.
  3. Makes your code easy to test.
  4. Enables faster debugging.
  5. It?s is entirely platform independent - you can easily use the same business logic and share it between apps for multiple platforms (iOS, tvOS, etc.).
Architecture Components
How it works?

  1. The View/View Controller sends events (The View Model's inputs) to the View Model.

  2. The View Model creates an Action from the received inputs and dispatch them to the Store.

  3. The View Model can use a dedicated Action Creators to create Actions. Action Creators do can async work and, based on the results it gets, returns different Actions to the View Model to dispatch.

  4. The Store sends the App State and the received Action to the Reducer.

  5. The Reducer receives the current App State and the dispatched Action, computes and returns new App State.

  6. The Store sends the new App State to the subscribers.

  7. One of the subscribers could be a Middleware that logs the App State resulted from dispatching an Action.

  8. The View Model receives the new App State, transform it presentable data, and send it to the View/View Controller.

  9. The View Model can use Transformers to transform the App State to presentable data. This helps you reuse the transformation code in different View Models.

  10. The View/View Controller render the UI to show the presentable data to the user.

How does RxState helps you build the Architecture?

RxState defines the main component for you:

  1. Store: Contains the App State in the form of Driver<[SubstateType]>.

  2. SubstateType: A protocol that tags structs representing a substate. Ex.

ct TasksState: SubstateType {   
var tasks: [Task]
var addingTask: Bool

You can add a Substates to the App State by dispatching StoreAction.add(states: [SubstateType]).

tasksState = TasksState()
action = StoreAction.add(states: [tasksState])
e.dispatch(action: action)
  1. ActionType: A protocol that tags an Action. The Store has the following Actions:
ic enum StoreAction: ActionType {
/// Adds substates to the application state.
case add(states: [SubstateType])

/// Removes all substates in the application state.
case reset

  1. MainReducer: A reducer used by the Store's dispatch function to call the respective reducer based on the Action type.
mainReducer: MainReducer = { (state: [SubstateType], action: ActionType) -> [SubstateType] in
// Copy the `App State`
var state: [SubstateType] = state

// Cast to a specific `Action`.
switch action {
case let action as TasksAction:

    // Extract the `Substate`.
    guard var (tasksStateIndex, tasksState) = state
        .enumerated()
        .first(where: { (_, substate: SubstateType) -> Bool in
            return substate is Store.TasksState}
        ) as? (Int, Store.TasksState)
        else {
            fatalError("You need to register `TasksState` first")
    }

    // Reduce the `Substate` to get a new `Substate`.
    tasksState = Store.reduce(state: tasksState, action: action)

    // Replace the `Substate` in the `App State` with the new `Substate`.
    state[tasksStateIndex] = tasksState as SubstateType

default:
    fatalError("Unknown action type")
}

// Return the new `App State`
return state

  1. MiddlewareType: A protocol defining an object that can observe the App State and the last dispatched Action and does something with it like logging:
ocol LoggingMiddlewareType: Middleware, HasDisposeBag {}

l class LoggingMiddleware: LoggingMiddlewareType {
var disposeBag = DisposeBag()

func observe(currentStateLastAction: Driver<CurrentStateLastAction>) {
    currentStateLastAction
        .drive(
            onNext: { (currentState: [SubstateType], lastAction: ActionType?) in
                print(currentState)
                print(lastAction)
            }, onCompleted: nil, onDisposed: nil)
        .disposed(by: disposeBag)
    }
}

Dependencies
Requirements
Installation
'RxState'
Demo

I have tried to make the demo app as comprehensive as possible. It currently runs on iOS and macOS. Notice how, because of the architecture, only the View/ View Controller layer needed to change in order to port the app from iOS to macOS.

Contributing

We would love to see you involved and feedback and contribution are greatly appreciated :) Checkout the Contributing Guide.

Influences and credits
Author

Nazih Shoura, shoura.nazeeh@gmail.com

License

This library belongs to RxSwiftCommunity.

RxState is available under the MIT license. See the LICENSE file for more info.


This work is supported by the National Institutes of Health's National Center for Advancing Translational Sciences, Grant Number U24TR002306. This work is solely the responsibility of the creators and does not necessarily represent the official views of the National Institutes of Health.