Redux for Swift - a predictable state container for Swift apps

Overview

ReduxKit

Build Status Code coverage status Doc coverage Carthage Version Cocoapods Compatible Platform License MIT

Merge / deprecation announcement:

ReduxKit and Swift-Flow have joined forces! The result is ReSwift.

The nitty gritty: We decided to deprecate ReduxKit and keep it as a reference implementation of how an almost exact Redux implementation in Swift can be accomplished.

Swift-Flow has adopted the name ReSwift and moved to it's new home as a nod to it's Redux roots that remain at it's core. Going forward, our combined efforts will be focused on ReSwift and surrounding tooling.

ReduxKit:

  • Will no longer be actively maintained
  • Will remain as a reference implementation of Redux in Swift
  • Pull requests are still welcome

What are you waiting for? Go get started with ReSwift today!

Introduction

ReduxKit is a swift implementation of the JavaScript Redux library by Dan Abramov and the React Community. ReduxKit stays as close as possible to Redux while bringing in Swift ways of doing things where appropriate.

A thorough walk through and description of the framework can be found at the official Redux repository: Redux.

It is currently implemented in a few swift apps and is frequently updated. Additions, middleware and help will be very much appreciated! So if you're trying it out and have any suggestions - feel free to post an issue and I'll be on it.

Contents

API

Quick start

Installing with Carthage

The easiest way to include ReduxKit is via Carthage:

iOS 8.0 required

Add ReduxKit to Cartfile

github "ReduxKit/ReduxKit" ~> 0.1

Run in terminal:

$ carthage update

Installing with CocoaPods

Add ReduxKit to your Podfile:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'

pod 'ReduxKit', '~> 0.1'

Then, run the following command:

$ pod install

The Gist

A more advanced Swift example of the original gist

import ReduxKit

/**
 This is an extremely simple and flexible action. The only requirement for actions is that they
 conform to the Action protocol.

 The Action protocol can be inherited from for app specific Action requirements. For a good example
 of this, see FluxStandardAction and the implementing types in the source.

 Action can be implemented as an enum, struct or class.
 */
struct IncrementAction: Action {
    let payload: Int
    init(payload: Int = 1) {
        self.payload = payload
    }
}


/**
 * And implemented as an enum action
 */
enum CountEnumAction: Action {
    case Increment
    case Decrement
    case Set(Int)
}



/**
 This is a simple reducer. It is a pure function that follows the syntax (State, Action) -> State.
 It describes how an action transforms the previous state into the next state.

 Instead of using the Action.type property - as is done in the regular Redux framework we use the
 power of Swifts static typing to deduce the action.
 */
func counterReducer(previousState: Int?, action: Action) -> Int {
    // Declare the reducers default value
    let defaultValue = 0
    var state = previousState ?? defaultValue

    switch action {
        /// Handling an action implemented as a struct
        case let action as IncrementAction:
            return state + action.payload
        // Handling actions implemented as Enums
        case CountEnumAction.Increment:
            return AppState(count: state.count + 1)
        case CountEnumAction.Decrement:
            return AppState(count: state.count - 1)
        case CountEnumAction.Set(let value):
           return AppState(count: value)


        default:
            return state
    }
}

/**
 The applications state. This should contain the state of the whole application.
 When building larger applications, you can optionally assign complex structs to properties on the
 AppState and handle them in the part of the application that uses them.
 */
struct AppState {
    var count: Int!
}

/**
 Create the applications reducer. While we could create a combineReducer function we've currently
 chosen to allow reducers to be statically typed and accept static states - instead of Any - which
 currently forces us to define the application reducer as such. This could possibly be simplified
 with reflection.
 */
let applicationReducer = {(state: AppState? = nil, action: Action) -> AppState in

    return AppState(
        count: counterReducer(state?.count, action: action),
    )
}

// Create application store. The second parameter is an optional default state.
let store = createStore(applicationReducer, nil)

let disposable = store.subscribe { state in
    print(state)
}


store.dispatch(IncrementAction())
// {counter: 1}

store.dispatch(CountEnumAction.Increment)
// {counter: 2}

store.dispatch(CountEnumAction.Decrement)
// {counter: 1}

// Dispose of the subscriber after use.
disposable.dispose()

License

MIT

Credits

Aleksander Herforth Rendtslev - @arendtslev
Karl Bowden - @karlbowden

Comments
  • Remove RxSwift dependency into an external custom implementation of the Store

    Remove RxSwift dependency into an external custom implementation of the Store

    Currently the store utilizes RxSwift to add, manage and dispatch to subscribers. While this gives us the whole range of RxSwift functions for free it might make sense to remove this dependency and create an optional store implementation in an external repository instead.

    enhancement help wanted suggestion 
    opened by Aleksion 21
  • Simpler Actions

    Simpler Actions

    Hi,

    I start using your library, and quickly I starting thinking if Actions shouldn't be a simpler struct, something just with a type, and everything else should be implemented by devs.

    In my case, I'm not using meta or error but my actions always have those fields, should I change the way I'm doing stuff or that makes sense to simplify the Actions?.

    Example:

    public protocol Action {
        var type: String { get }
    }
    
    public extension Action {
        public var type: String { return "\(self.dynamicType.self)" }
    }
    
    enhancement 
    opened by lucoceano 9
  • Async middlewares should travel the whole middleware chain again when store.dispatch is called

    Async middlewares should travel the whole middleware chain again when store.dispatch is called

    Currently a store.dispatch call in a middleware works - it calls the dispatch function AFTER the whole middleware chain is complete. However, it does not travel the middleware chain again. I have to reverse engineer the current ReduxJs implementation and figure out how they do it.

    bug enhancement help wanted 
    opened by Aleksion 7
  • Does ApplyMiddleware work with custom StateStreams

    Does ApplyMiddleware work with custom StateStreams

    I noticed that the subscribe function that's provided to the middlewareApi's is set to the default implementation:

                let middlewareApi = Store(
                    dispatch: { dispatch($0) },
                    subscribe: { _ in SimpleReduxDisposable(disposed: { false }, dispose: {}) },
                    getState: store.getState)
    

    Previously the middlewareApi didn't have access to the subscribe function - and I don't know whether we need it.

    But I fail to see how that function is actually doing anything atm?

    question 
    opened by Aleksion 5
  • Update GitHub pages with new Readme

    Update GitHub pages with new Readme

    Looks like the github pages branch is now broken after all of the moving around. It would also make a good place to drop in a lightly styled version of the readme.

    opened by agentk 5
  • Convert depending libraries to the new github target

    Convert depending libraries to the new github target

    @agentk - I'm currently wrapping up work before christmas - so I will fix this myself tomorrow evening. But if you have time to do this before me, please don't hesitate to fix them.

    Also regarding the naming - i've but it up as a suggestion. Let me know what you think. Then lets rename all libraries to keep things consistent.

    I'll also review and test your pull request tomorrow and merge everything in when I finish my work sprint.

    Once again - welcome aboard @agentk - you've done great work.

    opened by Aleksion 3
  • Simplified actions

    Simplified actions

    Fixes #29

    • Rename existing Action to FluxStandardAction
    • Create new empty Action protocol
    • Test changes
    • Cleanup filenames and tests
    • Update documentation
    • Update playgrounds
    opened by agentk 2
  • Forbid Dispatch From Within Reducers

    Forbid Dispatch From Within Reducers

    Just as Redux we should forbid dispatching actions from within reducers, since reducers should be free of side-effects.

    I've just implemented this for Swift Flow and thought it would make sense to bring this into ReduxKit as well. I'm not sure if implementation & specs fit your style, so I'm looking forward to feedback!

    opened by Ben-G 2
  • Should streams handle triggering reducers themselves?

    Should streams handle triggering reducers themselves?

    I noticed that with the implementation of streamFactories you changed how the reducers were triggered.

    I the original implementation this was done by RxSwift as such:

        // have the stateSubject subscribe to the dispatcher
        subjectDispatcher
            .scan(currentState, accumulator: reducer)
            .startWith(currentState)
            .subscribe(stateSubject)
    

    So it used observables to trigger reducers. In the updated version, you create a dispatch function - in the createStreamStore function - which calls stream.dispatch with an explicit call to the reducer as a parameter

        func dispatch(action: Action) -> Action {
            stream.dispatch(reducer(stream.getState(), action))
            return action
        }
    

    Should each individual stateStream handle how they trigger reducers themselves - or should we keep doing it manually in the createStreamStore function? Or should we simply pass the stream.dispatch function directly to the store and let the implementation details be handled in the stream implementation? I would like to document that one a bit better, so newcomers can understand how data flows through the framework. I can do that when we've decided on how to do it best.

    question suggestion 
    opened by Aleksion 2
  • Remove StateStream types

    Remove StateStream types

    [Docs] Remove StateStream from the README [Docs] Move strongly typed examples into HeaderDoc blocks [Tests] Test for and fix disposable.disposed getter [StateStream] Remove StateStream from core [StoreType] StoreType is no longer a subtype of MiddlewareApiType

    opened by agentk 1
  • Promote Swift-Flow

    Promote Swift-Flow

    @Ben-G has done an awesome job of developing a very idiomatic swift implementation of redux.js as Swift-Flow. After a short discussion it's clear we all agree that collaboration and sharing of idea's will be beneficial to both projects.

    opened by agentk 0
  • Compose function should be able to compose storeEnhancers (applyMiddleware etc).

    Compose function should be able to compose storeEnhancers (applyMiddleware etc).

    Currently compose is used in applyMiddleware:

    
            var dispatch: Dispatch!
    
            let store = next(reducer, initialState)
    
            let middlewareApi = MiddlewareApi(
                dispatch: { dispatch($0) },
                getState: store.getState)
    
            let middlewareChain = middleware.map { $0(middlewareApi) }
    
            dispatch = compose(middlewareChain)(store.dispatch)
    
    

    For reference:

    public typealias Dispatch = Action -> Action
    public typealias DispatchTransformer = Dispatch -> Dispatch
    
    

    in this case it composes DispatchTransformers: Dispatch -> Dispatch. So it iterates over each dispatch function and returns its result to the next. Finally it is called with the store's dispatch function - and the dispatch has been enhanced.

    However, this doesn't seem to be working with the more complex

     typealias StoreEnhancer = StoreCreator -> StoreCreator
    

    The following:

    let composedStoreEnhancer = compose([applyMiddleware([]), applyMiddleware([])]);
    

    Results in:

    error: cannot convert call result type '(((State?, Action) -> State, State?) -> Store<State>) -> (((State?, Action) -> State, State?) -> Store<State>)' to expected type '_ -> _'
    

    I am currently trying to find the root of the error (I suspected it was because It couldn't deduce the generic type - but even with out generics it seems to fail on me).

    opened by Aleksion 4
  • Simplify reactive framework bindings down to protocol enhancements

    Simplify reactive framework bindings down to protocol enhancements

    The seperate libraries for reactive framework bindings could be simplified and deprecated in favour of Delta style framework support.

    This may not fit in completely with the createStore func, but we should be able to get pretty close.

    enhancement 
    opened by agentk 1
  • Store should probably be class not struct

    Store should probably be class not struct

    There are a lot of articles on the internet, however I honestly fail to find definitive answer on the question "class or struct?"

    But considering ReduxKit I can foresee the following examples:

    1. First of all, I can clearly imagine the case where developer wants to pass a store down in view/viewController hierarchy
    2. Secondly, current store implementation is kinda leaky, because if you type it with State: Int it will work as "expected": copy of the store will be created and will exist independently. On the other hand, if one wants State: NSDictionary (or any other reference type) the store will be copied but it will share state with original one!

    P.S. Haven't tried it myself so my theory exercise might be wrong :blush:

    opened by delebedev 6
  • Should we support online playgrounds on IBM's Swift Sandbox?

    Should we support online playgrounds on IBM's Swift Sandbox?

    IBM's Swift Sandbox has a very early Codepen feel to it. It's pretty easy to concatenate all the swift files together and paste them into a Sandbox an provide a link to it. This way anybody is able to play with ReduxKit in the browser. I don't see an easy way of automating it yet though.

    Ie: http://swiftlang.ng.bluemix.net/#/repl/6de200cbf2a58190c59e5152ba7cc4716e7d7a5d7f2bff890514a422bd166756

    It's not perfect. You still have to scroll a long way down to see the final code. We could strip all the comments to make it smaller (and less understandable). I like where they are heading with it though. Maybe we should just keep an eye on it for once it's automated? (ala travis-ci)

    question suggestion 
    opened by agentk 1
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
🤖 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
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
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

null 7.3k Dec 25, 2022
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

null 7.3k Jan 3, 2023
Sample iOS application in SwiftUI presenting Redux architecture

SwiftUI-Redux-Demo Sample iOS application in SwiftUI presenting Redux architecture. My full article about Redux in detail you will find here: Redux ar

Wojciech Kulik 25 Nov 27, 2022
💎 Redux like architecture for SwiftUI

Simple Architecture like Redux Installation SPM dependencies: [ .package(url: "https://github.com/gre4ixin/ReduxUI.git", .upToNextMinor(from: "1.0

Pavel 38 Dec 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
🟣 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
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

SeungYeop Yeom 41 Dec 20, 2022
A powerful, minimal and composable architecture for building reactive iOS apps with SwiftUI or UIKit

SourceArchitecture A simple yet powerful framework for reactive programming with only a minimal optimized set of types. Sources are self-contained, hi

Daniel Hall 6 Nov 1, 2022
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

Majid Jabrayilov 104 Dec 26, 2022
Reactive Programming in Swift

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

ReactiveX 23.1k Jan 5, 2023
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.

Thane Gill 8 Jun 28, 2020