Predictable state container for Swift too

Overview

ReduxSwift

ReduxSwift is a minimal Swift port of Redux, a popular JavaScript library for application state management.

Swift version Carthage compatible Platforms Release License

Functionality
  • Centralized State
  • Unidirectional Data Flow
  • Functional Reactive Programming
Implementation
  • Type Safe
  • Extensible
  • Unobtrusive

Getting Started

"The whole state of your app is stored in an object tree inside a single store. The only way to change the state tree is to emit an action, an object describing what happened. To specify how the actions transform the state tree, you write pure reducers. That's it!" - Redux's Documentation

Application State

The Application State is a container that stores all information needed for your app to render. Since it's only a data container, it can be of any type from a single Int to a complex Struct.

struct AppState {
    struct Todo {
        let text: String
        var done: Bool
    }

    var todos: [Todo] = []
}
Tips
  • Structs are recommended for complex state structures, since they guarantee the reducer can't modify the state directly
  • State should be as minimal as possible, which means cheaply derivable data should be excluded from it

Actions

Actions are structs that describe changes to be made to the Application State. Since actions are the only way to change it, the action set represents all ways your app can change its own state. The only requirement for your action types are that they follow the ActionType protocol.

struct ADD_TODO: ActionType {
    let text: String
}

struct DO_TODO: ActionType {
    let id: Int
}

struct CLEAN_DONE: ActionType {

}
Tips
  • Although classes can be used to define Actions as well, little data containers like that benefit from the pass-by-value behavior offered by structs
  • Actions should carry all information needed by the reducer to make the actual change (more on that next).

Reducers

Reducers are pure functions that take an action and the current state (if any), and return a new state. Reducers are required to initialize the state in case of absence and must always return a state instance. Since these functions must be pure, all data needed for the change to be made have to be included in the action struct.

func reducer(action: ActionType, state: AppState?) -> AppState {
    var state = state ?? AppState.init()

    switch action {

    case let a as ADD_TODO:
        let todo = AppState.Todo(text: a.text, done: false)
        state.todos.append(todo)

    case let a as DO_TODO:
        state.todos[a.id].done = true

    case _ as CLEAN_DONE:
        let todos = state.todos.filter{!$0.done}
        state.todos = todos

    default: break
    }

    return state
}
Tips
  • Reducers can be composed together, making it possible to modularize your state architecture
  • Reducers cannot have side-effects, so if you need asynchronous action dispatching you might need to check out some middleware for it

Fire it up

The State, Actions and Reducers come together in the Store creation. It exposes the current state through the 'state' property and a single method 'dispatch' used for dispatching actions.

let store = Store<AppState>.init(reducer: reducer, state: nil, middlewares: [])

// State.todos => []
store.dispatch(ADD_TODO(text: "Code some Swift!"))
// State.todos => [{text "Code some Swift!", done false}]
store.dispatch(ADD_TODO(text: "Code more Swift!"))
// State.todos => [{text "Code some Swift!", done false}, {text "Code more Swift!", done false}]
store.dispatch(DO_TODO(id: 0))
// State.todos => [{text "Code some Swift!", done true}, {text "Code more Swift!", done false}]
store.dispatch(CLEAN_DONE())
// State.todos => [{text "Code more Swift!", done false}]
Tips
  • The Store can be initialized with a State, making it easy to save and restore it
  • Middlewares can be used to enhance the Store functionality (more on that next)

Going deeper

You just learned about the core concepts of the Redux architecture. Next we'll talk about two middlewares included in this framework that extend the store functionality to help you integrate it in your application.

Subscriptions

Everything we learned until now would be pointless if you didn't have a way to get notified about state changes whenever they happen. This middleware lets you do that in a cool reactive programming way.

let subs = Subscriptions<AppState, Int>.init()
let subsStore = Store<AppState>.init(reducer: reducer, state: nil, middlewares: [subs.middleware])

subs.subscribe(key: 1) {state in
    print("state changed!")
}

subs.unsubscribe(key: 1)
Tips
  • Your subscriber id can be of whatever Hashable type, as long as you identify it as the second generic parameter (Int in the previous example)
  • Did you know UIView's are Hashable?

Thunk

This is a basic middleware that enables asynchronous dispatching. It works alongside an action type called THUNK, that takes a function that takes the Store as it's first parameter. That way you can dispatch an function that will be responsible for dispatching other actions.

let thunk = Middlewares<AppState>.thunk
let thunkStore = Store<AppState>.init(reducer: reducer, state: nil, middlewares: [thunk])

thunkStore.dispatch(THUNK<AppState>{store in
    _ = store.dispatch(CLEAR_DONE())
})
Tips
  • Your thunk function can use the store reference however they want, including through in background routines
  • Thunks are just a basic way of inverting the control flow of the Store. For complex asynchronous workflows you might need to check elsewhere.

Logger

This middleware is a developer helper that logs action and state in down and up stream through the middleware chain. That means it will print to console all actions dispatched and the states before and after they were reduced.

let logger = Middlewares<AppState>.logger
let loggedStore = Store<AppState>.init(reducer: reducer, state: nil, middlewares: [logger])

Installation

Carthage

This is the recommended way of installing this package.

Carthage is an awesome dependency manager that'll take care of your dependencies without getting in your way. To install ReduxSwift through Carthage, just follow these steps:

  • Add the following line to your Cartfile
github "lsunsi/ReduxSwift"
  • Run the following command to fetch and build your dependencies
$ carthage update
  • Drag the compiled framework from the Carthage folder into your project
  • Rejoice!

Manually

If you prefer to install this package manually, just follow these steps:

  • Make sure your project is a git repository. If it isn't, just run this command from your project root folder:
$ git init
  • Add ReduxSwift as a git submodule by running the following command.
$ git submodule add https://github.com/lsunsi/ReduxSwift.git
  • Drag the ReduxSwift.xcodeproj from the newly created ReduxSwift folder to your project.

It should be nested in your actual project in the Project Navigator.

  • Add ReduxSwift to your Embedded Binaries.

Select your project from the Project Navigator. Then, select your Target and you'll see an Embedded Binaries menu in the General tab.

  • Rejoice!

License

This project is licensed under the MIT License - see the LICENSE file for details

Acknowledgments

  • Dan Abramov for bringing Redux to our lives
You might also like...
A super simple library for state management with unidirectional data flow.
A super simple library for state management with unidirectional data flow.

OneWay 🚧 OneWay is still experimental. As such, expect things to break and change in the coming months. OneWay is a super simple library for state ma

Unidirectional flow implemented using the latest Swift Generics and Swift Concurrency features.

swift-unidirectional-flow Unidirectional flow implemented using the latest Swift Generics and Swift Concurrency features. struct SearchState: Equatabl

Reactive Programming in Swift
Reactive Programming in Swift

Rx is a generic abstraction of computation expressed through ObservableElement interface, which lets you broadcast and subscribe to values and other

RxSwift extentions for Swift optionals and "Occupiable" types

RxOptional RxSwift extentions for Swift optionals and "Occupiable" types. Usage All operators are available on Driver as well unless otherwise marked.

Unidirectional Data Flow in Swift - Inspired by Redux
Unidirectional Data Flow in Swift - Inspired by Redux

ReSwift Supported Swift Versions: Swift 4.2, 5.x For Swift 3.2 or 4.0 Support use Release 5.0.0 or earlier. For Swift 2.2 Support use Release 2.0.0 or

A Swift Reactive Programming Kit
A Swift Reactive Programming Kit

ReactiveKit is a lightweight Swift framework for reactive and functional reactive programming that enables you to get into the reactive world today. T

RxSwift wrapper around the elegant HTTP networking in Swift Alamofire

RxAlamofire RxAlamofire is a RxSwift wrapper around the elegant HTTP networking in Swift Alamofire. Getting Started Wrapping RxSwift around Alamofire

An array class implemented in Swift that can be observed using ReactiveCocoa's Signals
An array class implemented in Swift that can be observed using ReactiveCocoa's Signals

ReactiveArray An array class implemented in Swift that can be observed using ReactiveCocoa's Signals. Installation Carthage Add the following to your

Simple and lightweight Functional Reactive Coding in Swift for the rest of us
Simple and lightweight Functional Reactive Coding in Swift for the rest of us

The simplest ObservableT implementation for Functional Reactive Programming you will ever find. This library does not use the term FRP (Functional R

Comments
  • Manual Installation

    Manual Installation

    CocoaPods and Carthage are awesome tools and make our life really easier, but there are some devs who still don't know how to use them.

    It would be cool to add the Manual installation guide in your README.md. You can take a look at my iOS Readme Template to see how you can do it.

    opened by lfarah 1
  • Updated project, source, and tests to Swift 3.0

    Updated project, source, and tests to Swift 3.0

    Updated cartfile to use 3.0 versions for both Quick & Nimble. Had to remove typealias Middleware since Swift 3.0 doesn't allow @escaping in type aliases

    opened by regynald 1
Owner
Lucas Sunsi Abreu
Software Developer. I code on the weekends.
Lucas Sunsi Abreu
RxReduce is a lightweight framework that ease the implementation of a state container pattern in a Reactive Programming compliant way.

About Architecture concerns RxReduce Installation The key principles How to use RxReduce Tools and dependencies Travis CI Frameworks Platform Licence

RxSwift Community 125 Jan 29, 2022
A barebone, thread-safe Redux-like container for Swift.

SwiftTinyRedux SwiftTinyRedux is a barebone, thread-safe Redux-like container for Swift. It features a minimal API and supports composable reducers. I

Valentin Radu 9 Nov 13, 2022
Unidirectional State Management Architecture for Swift - Inspired by Vuex and Flux

Unidirectional State Management Architecture for Swift - Inspired by Vuex and Flux Introduction VueFlux is the architecture to manage state with unidi

Ryo Aoyama 324 Dec 17, 2022
MVVM + FLUX iOS Instagram client in Swift, eliminates Massive View Controller in unidirectional event/state flow manner

CZInstagram MVVM + FLUX iOS Instagram client in Swift, eliminates Massive View Controller in unidirectional event/state flow manner. Unidirectional Da

Cheng Zhang 56 Nov 1, 2022
Elegant state machine for Swift.

SwiftState Elegant state machine for Swift. Example enum MyState: StateType { case state0, state1, state2 } // setup state machine let machine = S

ReactKit 885 Dec 16, 2022
🤖 RxSwift + State Machine, inspired by Redux and Elm.

RxAutomaton RxSwift port of ReactiveAutomaton (State Machine). Terminology Whenever the word "signal" or "(signal) producer" appears (derived from Rea

Yasuhiro Inami 719 Nov 19, 2022
🟣 Verge is a very tunable state-management engine on iOS App (UIKit / SwiftUI) and built-in ORM.

Verge.swift ?? An effective state management architecture for iOS - UIKit and also SwiftUI ?? _ An easier way to get unidirectional data flow _ _ Supp

VergeGroup 478 Dec 29, 2022
CMPSC475 Final Project, ArboretumID Application allows users to explore the Penn State Arboretum, identify plants and learn about the exhibits!

ArboretumID: CMPSC475 Final Project Taylan Unal (@taylanu) About ArboretumID ArboretumIdentifier (ArboretumID) is an app that enhances the Penn State

Taylan 1 Nov 27, 2021
🌾 Harvest: Apple's Combine.framework + State Machine, inspired by Elm.

NOTE: This repository has been discontinued in favor of Actomaton. ?? Harvest Apple's Combine.framework (from iOS 13) + State Machine, inspired by Elm

Yasuhiro Inami 386 Dec 18, 2022
An experimental time traveling state store for SwiftUI

SwiftUI Time Travel A SwiftUI state store and view that allow you to scrub through an application's state. This is a super rough prototype: it's only

Tim Donnelly 139 Sep 14, 2022