Need help with RxState?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

RxSwiftCommunity
140 Stars 10 Forks Other 33 Commits 1 Opened issues

Description

Redux implementation in Swift using RxSwift

Services available

!
?

Need anything else?

Contributors list

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

  • App State: A single immutable data structure. It includes the UI state, the navigation state and the state of your model layer.

  • Store:Contains the app state and notifies the

    App State Observers
    of the
    App State
    updates.
  • Reducer: A pure function that takes the current app state and an

    Action
    as input, creates a new
    App State
    that reflects the changes described by the
    Action
    , and returns the new
    App State
    .
  • Action: Actions describe a state change. The only way to modified the

    App State
    is by dispatching
    Actions
    to the
    Store
    .
  • Action Creators and Dispatchers: Creates

    Action
    s and dispatch them to the store.
  • App State Observers: Observers the

    App State
    in the
    Store
    to transform it to presentable data, write logs, etc.
  • View: Presents the presentable data that was deriver from the

    App State
    and delivers the user's interactions to the
    Action Creators and Dispatchers
    .

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
    .
  • The
    View Model
    can use a dedicated
    Action Creator
    s to create
    Action
    s.
    Action Creator
    s do can async work and, based on the results it gets, returns different
    Action
    s to the
    View Model
    to dispatch.
  1. The

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

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

    Store
    sends the new
    App State
    to the subscribers.
  • One of the subscribers could be a
    Middleware
    that logs the
    App State
    resulted from dispatching an
    Action
    .
  1. The
    View Model
    receives the new
    App State
    , transform it presentable data, and send it to the
    View/View Controller
    .
  • The
    View Model
    can use
    Transformer
    s to transform the
    App State
    to presentable data. This helps you reuse the transformation code in different
    View Model
    s.
  1. 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
    .
  2. SubstateType
    : A protocol that tags structs representing a substate. Ex.
struct TasksState: SubstateType {   
    var tasks: [Task]
    var addingTask: Bool
}

You can add a

Substate
s to the
App State
by dispatching
StoreAction.add(states: [SubstateType])
.
let tasksState = TasksState()
let action = StoreAction.add(states: [tasksState])
store.dispatch(action: action)
  1. ActionType
    : A protocol that tags an
    Action
    . The
    Store
    has the following
    Action
    s:
public 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.
let 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:
protocol LoggingMiddlewareType: Middleware, HasDisposeBag {}

final 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

  • Swift 5

Installation

pod 'RxState'

Create a

Package.Swift
file in your project's root folder.

Add following content into the

Package.swift
file
// swift-tools-version:5.0

import PackageDescription

let package = Package( name: "YourProjectName", dependencies: [ .package(url: "https://github.com/RxSwiftCommunity/RxState.git", from: "0.6.0") ], targets: [ .target(name: "YourProjectTarget", dependencies: ["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! Feedback and contribution are greatly appreciated :) Checkout the Contributing Guide and the Code of Conduct.

Influences and credits

  • RxSwift: Reactive Programming in Swift.
  • Redux: a predictable state container for JavaScript apps.

Author

Nazih Shoura, [email protected]

License

This library belongs to RxSwiftCommunity.

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

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.