Predictable state management for SwiftUI applications.

Overview

SwiftDux

Predictable state management for SwiftUI applications.

Swift Version Platform Versions Github workflow codecov

SwiftDux is a state container inspired by Redux and built on top of Combine and SwiftUI. It helps you write applications with predictable, consistent, and highly testable logic using a single source of truth.

Installation

Prerequisites

  • Xcode 12+
  • Swift 5.3+
  • iOS 14+, macOS 11.0+, tvOS 14+, or watchOS 7+

Install via Xcode:

Search for SwiftDux in Xcode's Swift Package Manager integration.

Install via the Swift Package Manager:

import PackageDescription

let package = Package(
  dependencies: [
    .Package(url: "https://github.com/StevenLambion/SwiftDux.git", from: "2.0.0")
  ]
)

Demo Application

Take a look at the Todo Example App to see how SwiftDux works.

Getting Started

SwiftDux helps build SwiftUI-based applications around an elm-like architecture using a single, centralized state container. It has 4 basic constructs:

  • State - An immutable, single source of truth within the application.
  • Action - Describes a single change of the state.
  • Reducer - Returns a new state by consuming the previous one with an action.
  • View - The visual representation of the current state.

State

The state is an immutable structure acting as the single source of truth within the application.

Below is an example of a todo app's state. It has a root AppState as well as an ordered list of TodoItem objects.

import SwiftDux

typealias StateType = Equatable & Codable

struct AppState: StateType {
  todos: OrderedState<TodoItem>
}

struct TodoItem: StateType, Identifiable {
  var id: String,
  var text: String
}

Actions

An action is a dispatched event to mutate the application's state. Swift's enum type is ideal for actions, but structs and classes could be used as well.

import SwiftDux

enum TodoAction: Action {
  case addTodo(text: String)
  case removeTodos(at: IndexSet)
  case moveTodos(from: IndexSet, to: Int)
}

Reducers

A reducer consumes an action to produce a new state.

final class TodosReducer: Reducer {

  func reduce(state: AppState, action: TodoAction) -> AppState {
    var state = state
    switch action {
    case .addTodo(let text):
      let id = UUID().uuidString
      state.todos.append(TodoItemState(id: id, text: text))
    case .removeTodos(let indexSet):
      state.todos.remove(at: indexSet)
    case .moveTodos(let indexSet, let index):
      state.todos.move(from: indexSet, to: index)
    }
    return state
  }
}

Store

The store manages the state and notifies the views of any updates.

import SwiftDux

let store = Store(
  state: AppState(todos: OrderedState()),
  reducer: AppReducer()
)

window.rootViewController = UIHostingController(
  rootView: RootView().provideStore(store)
)

Middleware

SwiftDux supports middleware to extend functionality. The SwiftDuxExtras module provides two built-in middleware to get started:

  • PersistStateMiddleware persists and restores the application state between sessions.
  • PrintActionMiddleware prints out each dispatched action for debugging purposes.
import SwiftDux

let store = Store(
  state: AppState(todos: OrderedState()),
  reducer: AppReducer(),
  middleware: PrintActionMiddleware())
)

window.rootViewController = UIHostingController(
  rootView: RootView().provideStore(store)
)

Composing Reducers, Middleware, and Actions

You may compose a set of reducers, actions, or middleware into an ordered chain using the '+' operator.

// Break up an application into smaller modules by composing reducers.
let rootReducer = AppReducer() + NavigationReducer()

// Add multiple middleware together.
let middleware = 
  PrintActionMiddleware() +
  PersistStateMiddleware(JSONStatePersistor()

let store = Store(
  state: AppState(todos: OrderedState()),
  reducer: reducer,
  middleware: middleware
)

ConnectableView

The ConnectableView protocol provides a slice of the application state to your views using the functions map(state:) or map(state:binder:). It automatically updates the view when the props value has changed.

struct TodosView: ConnectableView {
  struct Props: Equatable {
    var todos: [TodoItem]
  }

  func map(state: AppState) -> Props? {
    Props(todos: state.todos)
  }

  func body(props: OrderedState<Todo>): some View {
    List {
      ForEach(todos) { todo in
        TodoItemRow(item: todo)
      }
    }
  }
}

ActionBinding<_>

Use the map(state:binder:) method on the ConnectableView protocol to bind an action to the props object. It can also be used to bind an updatable state value with an action.

struct TodosView: ConnectableView {
  struct Props: Equatable {
    var todos: [TodoItem]
    @ActionBinding var newTodoText: String
    @ActionBinding var addTodo: () -> ()
  }

  func map(state: AppState, binder: ActionBinder) -> OrderedState<Todo>? {
    Props(
      todos: state.todos,
      newTodoText: binder.bind(state.newTodoText) { TodoAction.setNewTodoText($0) },
      addTodo: binder.bind { TodoAction.addTodo() }
    )
  }

  func body(props: OrderedState<Todo>): some View {
    List {
      TextField("New Todo", text: props.$newTodoText, onCommit: props.addTodo) 
      ForEach(todos) { todo in
        TodoItemRow(item: todo)
      }
    }
  }
}

Action Plans

An ActionPlan is a special kind of action that can be used to group other actions together or perform any kind of async logic outside of a reducer. It's also useful for actions that may require information about the state before it can be dispatched.

/// Dispatch multiple actions after checking the current state of the application.
let plan = ActionPlan<AppState> { store in
  guard store.state.someValue == nil else { return }
  store.send(actionA)
  store.send(actionB)
  store.send(actionC)
}

/// Subscribe to services and return a publisher that sends actions to the store.
let plan = ActionPlan<AppState> { store in
  userLocationService
    .publisher
    .map { LocationAction.updateUserLocation($0) }
}

Action Dispatching

You can access the ActionDispatcher of the store through the environment values. This allows you to dispatch actions from any view.

struct MyView: View {
  @Environment(\.actionDispatcher) private var dispatch

  var body: some View {
    MyForm.onAppear { dispatch(FormAction.prepare) }
  }
}

If it's an ActionPlan that's meant to be kept alive through a publisher, then you'll want to send it as a cancellable. The action below subscribes to the store, so it can keep a list of albums updated when the user applies different queries.

extension AlbumListAction {
  var updateAlbumList: Action {
    ActionPlan<AppState> { store in
      store
        .publish { $0.albumList.query }
        .debounce(for: .seconds(1), scheduler: RunLoop.main)
        .map { AlbumService.all(query: $0) }
        .switchToLatest()
        .catch { Just(AlbumListAction.setError($0) }
        .map { AlbumListAction.setAlbums($0) }
    }
  }
}

struct AlbumListContainer: ConnectableView {
  @Environment(\.actionDispatcher) private var dispatch
  @State private var cancellable: Cancellable? = nil
  
  func map(state: AppState) -> [Album]? {
    state.albumList.albums
  }

  func body(props: [Album]) -> some View {
    AlbumsList(albums: props).onAppear { 
      cancellable = dispatch.sendAsCancellable(AlbumListAction.updateAlbumList)
    }
  }
}

The above can be further simplified by using the built-in onAppear(dispatch:) method instead. This method not only dispatches regular actions, but it automatically handles cancellable ones. By default, the action will cancel itself when the view is destroyed.

struct AlbumListContainer: ConnectableView {
  
  func map(state: AppState) -> [Album]? {
    Props(state.albumList.albums)
  }

  func body(props: [Album]) -> some View {
    AlbumsList(albums: props).onAppear(dispatch: AlbumListAction.updateAlbumList)
  }
}

Previewing Connected Views

To preview a connected view by itself use the provideStore(_:) method inside the preview.

#if DEBUG
public enum TodoRowContainer_Previews: PreviewProvider {
  static var store: Store<TodoList> {
    Store(
      state: TodoList(
        id: "1",
        name: "TodoList",
        todos: .init([
          Todo(id: "1", text: "Get milk")
        ])
      ),
      reducer: TodosReducer()
    )
  }
  
  public static var previews: some View {
    TodoRowContainer(id: "1")
      .provideStore(store)
  }
}
#endif
Comments
  • Handling modals

    Handling modals

    Hello,

    I'm trying to build an app with a modal window using SwiftUI .sheet functionality. It uses bindings and I can't wrap my head over the correct way of integrating it into SwiftDux project. Right now I update the binding right inside the map function which is obviously not the right way to go.

    Any hints?

    opened by Nek 7
  • Consider removing Codable requirement for State

    Consider removing Codable requirement for State

    Is your feature request related to a problem? Please describe. I'm using SwiftDux with a shared model in Kotlin-Native. This poses challenges with the Codable requirement.

    Describe the solution you'd like Ideally the requirement of having State be Codable should be removed as it is by nature an extension of the core.

    Describe alternatives you've considered Wrapping the primary state type with a wrapper that is Codable. This adds a lot of indirection and unnecessary complexity IMO.

    opened by CoreyKaylor 6
  • Working with something like Core Data?

    Working with something like Core Data?

    Hi,

    I was wondering if you had any guidance on how to use this library with something like Core Data? I'm interested in core data b/c i have a relatively nontrivial amount of data, and it seems to jive with core data nicely. (Also, I guess the latest version gives you icloud sync for free? I haven't really gotten there yet. Anyway...)

    I can't seem to hold Core Data objects in the state (since they don't conform to StateType), so i'm creating extensions of the state objects to make queries against Core Data. Is that generally how you'd think Core Data would best be used?

    For example:

    extension ProjectState {
    
        var selectedProject: Project? {
            get {
                // selectedProjectId held in the swiftdux state
                guard let id = selectedProjectId else { return nil }
    
                let ctx = PersistenceManager.context
                let projFetch = PersistenceManager.projectFetchReq
                projFetch.predicate = NSPredicate(format: "id == %@", id as CVarArg)
    
                let projects = try! ctx.fetch(projFetch) as! [Project]
    
                return projects.count > 0 ? projects[0] : nil
            }
        }
    

    In any case, I thought I might ask - thanks very much for your library, again. It's freakin great.

    opened by hoopes 6
  • Views need to know about dispatched Actions

    Views need to know about dispatched Actions

    A common pattern is that the result of network requests or database queries will dispatch actions to the store to update some state. This abstracts the UI from having knowledge of services are responsible for setting the state.

    However, by default, a view doesn't update if it does not dispatch an action. To get the view to reflect state changes in the above scenarios, it has to implement the updateWhen() protocol method. While this is simpler than trying specify which part of the state to observe, which is tricky in ReSwift, it does require the View to check for (and therefore know about) specific actions that are dispatched by other services. This seems like an implementation detail the Views shouldn't know about.

    Perhaps this is something that can be resolved by dispatching generic actions though middleware?

    opened by lightandshadow68 5
  • watchOS setup

    watchOS setup

    When I tried to used SwiftDux for the watchOS application, it turns out that Package.swift is missing .watchOS(.v7), in the Platforms array and it uses the default watchOS version (something like v2) but this version doesn't have access to Combine.

    The second thing that I notice is #if canImport(UIKit) from SwiftDuxExtras is true on watchOS but it gives an error about UIApplication not found. So I added separated macro #if canImport(WatchKit) that should be called on watchOS and it's using WKExtension.applicationDidEnterBackgroundNotificatio notification

    opened by kamilpowalowski 4
  • Dispatch from reducer?

    Dispatch from reducer?

    Hi - first of all, this library is great, and I hope you keep working on it. I'm kind of a beginning in swift development, and this is realllllly helping me get my feet under me.

    I was wondering if it was possible to dispatch from a reducer function? My current playground use-case is there is a single AppState, and it has a bunch of sub-states. Each substate initializes itself, and the AppState keeps track of the master "the whole app is initialized" boolean. It also has a list of strings of the overall state of each sub-state init. So, when each sub-state changes its init status, or the list of strings describing it, I wanted to dispatch to the main app state to go inspect each sub-state, and update the global init flag and set of init messages to display.

    Obviously, I might be thinking about this all wrong, and there's already a solution for this that I'm not picking up on. If so, my apologies, and a pointer to that would be greatly appreciated.

    Again - keep it up, man. This is great.

    opened by hoopes 4
  • ActionPlan chaining

    ActionPlan chaining

    Hi! Is there a way that I can chain ActionPlan requests? Next action plan gets executed when all actions from the previous one get executed. For example:

    TodoAction.fetchTodos() .then(TodoAction.filterTodos()) .then(TodoAction.mapTodos())

    opened by hfabisiak 4
  • Cancel ActionPlan

    Cancel ActionPlan

    Hi,

    What would be the best way to cancel an actionPlan ?

    For example, I have a signup screen when I tap the signup button it start a networking call. Now the user is impatient and close the signup screen before the network call finishes. How can I cancel the signUpActionPlan in the view disappear callback ?

    enhancement 
    opened by AlexisQapa 4
  • ActionPlan that returns optional publisher?

    ActionPlan that returns optional publisher?

    In the example code for ActionPlan, I see it conditionally returning nil at the top and then returning an action publisher at the bottom, but I don't see any initializers that support that return type? Is that a typo in the example or am I not understanding correctly?

    https://github.com/StevenLambion/SwiftDux/blob/master/Sources/SwiftDux/Action/ActionPlan.swift#L11

    opened by aputinski 3
  • Question: Async actions (async thunk-like)

    Question: Async actions (async thunk-like)

    I recently discovered this project and I find it really promising, but checking readme file and example project I could not figure out how to perform an async action like async thunks in redux.

    For example if I need to perform a login with a remote service I would need an action with 2 parameters (id, pw) that should trigger a background request and set a loading= true and then upon completion report success or fail using 2 other actions.

    I thought Action Plans could be the right way but I am not sure about passing 2 parameters to an action plan.

    Maybe I am missing something? Thank you.

    opened by gioele-santi 3
  • dispatch from action handler

    dispatch from action handler

    Hi,

    I had asked a question in https://github.com/StevenLambion/SwiftDux/issues/28 where I was trying to find the proper incantation to dispatch another action from an action handler, but was looking for some clarification.

    Cribbing from the README a bit:

    
      func reduce(state: OrderedState<TodoItem>, action: TodoAction) -> OrderedState<TodoItem> {
        var state = state
        switch action {
        case .addTodo(let text):
          let id = UUID().uuidString
          state.append(TodoItemState(id: id, text: text))
          ////// DISPATCH ACTION HERE ///////
        }
        return state
      }
    }
    

    It doesn't appear that I can import the dispatch function like it's used in the views. I might not be using it right, though.

    Is there an obvious way to do this that I'm missing?

    A particular use-case I'm running into now is there are some number of actions that set individual flags, and a more general action that reads all of those flags for something else. (I can elaborate on that, if it doesn't make sense, or would help in any way.)

    Much obliged! FWIW, I'm currently using the swift-5.2 branch.

    opened by hoopes 3
  • [BUG] “OS X” target identifier is causing tests to fail on Github

    [BUG] “OS X” target identifier is causing tests to fail on Github

    *Describe the bug The target identifier for building against “OS X” no longer works inside GitHub Actions, it needs to be updated to the one used by macOS 11+

    opened by StevenLambion 1
Releases(v2.0.1)
  • v2.0.1(Nov 30, 2020)

    Fixes

    • The View.onAppear(dispatch:) method caused duplicate views. #54
    • RunnableActions were incorrectly dispatched to the store's reducer.
    Source code(tar.gz)
    Source code(zip)
  • v2.0.0(Nov 21, 2020)

    Deprecated

    • MappedDispatch should be replaced with @Environment(.\actionDisaptcher) to be consistent with Apple's usage. #50
    • MappedState should be replaced with ConnectableView or Connector. #50
    • StateType and IdentifiableState are deprecated in favor of having applications define their own protocol to adhere to. Instead of requiring an opinionated state type, the library uses conditional protocol adherences when necessary. #50
    • onAction(perform:) view modifier is deprecated in favor of using middleware. #50

    Changes

    • macOS 11.0 is now required. #50
    • Removed ActionType from the Store<_>'s didChange publisher. #48
    • Connector's internal publishing logic has been moved to StateStorable, so it may be reused elsewhere. #50
    • All actions may now be chained instead of just ActionPlans. #51
    • RunnableAction now returns an AnyPublisher<Action, Never> type instead of an AnyCancellable. #51
    • ActionPlan now requires a publisher to return from async blocks instead of providing a completion closure. #52
    • Middleware now return an optional Action instead of calling next(_:) on the StoreProxy<_>. #52

    Added

    • Added concrete StorePublisher in place of using AnyPublisher for store changes. #48
    • Added StateStorable protocol so that there's a shared way to extend the Store<> and StoreProxy<> types. #50
    • Added StateStorable.publish(_:) method as a single place to receive an updatable state object. #50

    Removed

    • StateBinder is removed. #48
    • Removed next(_:) from StoreProxy<_>. #52
    • Removed the completion closure from ActionSubscriber. #52

    Fixed

    • Fixed type inference with onAppear(dispatch:). #50
    • Fixed a circular dependency in the StoreProxy<_> given to middleware. #52
    • Fixed a bug introduced with iOS 14 that may cause a crash related to the onReceive view modifier. #52
    Source code(tar.gz)
    Source code(zip)
  • v1.2.4(Nov 10, 2020)

  • v1.2.3(Oct 14, 2020)

    Changed

    • Replaced the requirement for StateType and IdentifiableState by using conditional, granular constraints where needed. #46
    • NoopAnyStore is now a struct. #44
    • Views can now use protocols that the AppState adheres to to retrieve specific parts of the app state. #44
    Source code(tar.gz)
    Source code(zip)
  • v1.2.1(May 13, 2020)

  • v1.2.0(May 5, 2020)

    Changed

    • ActionBinder can now bind actions to callable functions. #40, #38
    • Replaced the old internal state connection API with a faster and simpler implementation. #39

    Removed

    • Removed Connectable API in favor of ConnectableView due to incompatibilities with the new internal, reference-free implementation. #39
    Source code(tar.gz)
    Source code(zip)
  • v1.1.0(Apr 11, 2020)

    Changed

    • Loosened the adherence to StateType at the Store level. #36
    • Simplified ActionDispatcher API by moving the proxy functionality to OnActionViewModifier. #32
    • Provided a more consistent Store API between reducers and middleware. #33
    • ActionDispatcher can now be called as a function. #32
    • MappedDispatch injects an ActionDispatcher instead of a function. #32
    • Modified the implementation of action plans to make it easier to include 3rd party ones. #36

    Added

    • Added ActionBinding<> to replace the Equatable extension on Binding<>. #30

    Removed

    • onAppearAsync and onDisappearAsync. #36
    Source code(tar.gz)
    Source code(zip)
  • v1.0.1(Jan 23, 2020)

  • v1.0.0(Jan 22, 2020)

    This marks the 1.0.0 release of the library. The API will be stabilized moving forward.

    Fixed

    • PersistStateMiddleware would save the state when the StoreAction<_>.reset was dispatched. #24
      • It no longer saves the state when the store is reset.
    • Connectable.updateWhen would stop a view from updating from its own dispatched actions. #24
      • If the method is implemented, both it and any dispatched actions will cause an update.
    • Connectable.updateWhen would fail to work in some cases when the first call returned true. #24
      • Returning true using any possible implementation will now work.

    Changed

    • State is now required to be equatable. #26
    • Middleware is now a protocol type. #25
    • StoreProxy.state is now a non-optional type. #24
    • Store, ActionDispatcher, and StatePersistor no longer adhere to Subscriber. #24
    • Store.didChange is now an AnyPublisher<Action, Never>. #24
    • PersistStateMiddleware now debounces for 1 second by default. #24
    • PrintActionMiddleware now accepts a new filter block. #24
    • onAppearAsync and onDisappearAsync are deprecated. #24
      • These were need to workaround a bug in a pervious beta of iOS 13. #24

    Added

    • ActionPlan has a new init block that accepts an AnyCancellable to support ActionSubsriber, #24
    • ActionSubscriber to dispatch actions from a publisher. #24
    • PersistSubscriber to persist state from a publisher. #24
    • StoreProxy.done for use in action plans to tell the proxy that it has completed. #24
    • TypedMiddleware and HandleActionMiddleware. #25
    Source code(tar.gz)
    Source code(zip)
  • v0.12.1(Jan 11, 2020)

    Fixed

    • Inconsistency with onMove(_:_:) method of OrderedState<_> #21

    Added

    • StateBinder API to create 2-way bindings between a state value and an action. #22
    Source code(tar.gz)
    Source code(zip)
  • v0.12.0(Jan 11, 2020)

    Changes

    • Action plans themselves now trigger the change event from the store. #19

    Added

    • API to chain action plans together. #20
    • API to call code blocks when an action plan completes. #20
    • onAppear(dispatch:cancelOnDisappear:) method on View #19
    Source code(tar.gz)
    Source code(zip)
  • v0.11.3(Nov 27, 2019)

    Fixed

    • Manually implemented coding protocol due to bugs with the synthesized version. #15
    • The SwiftExtra module now works on macOS without requiring Catalyst. #16

    Technical

    • Added macOS as a testing target. #16
    Source code(tar.gz)
    Source code(zip)
  • v0.11.2(Oct 3, 2019)

    Changes

    • Cleanup to documentation. #13

    Added

    • API to cancel publisher based ActionPlans. #14

    Technical

    • Code formatting via swift-format. #13
    • Moved documentation to gh-pages branch. #13
    • Set up CI linting and testing via Github workflows. #13
    • Added code coverage reports via codecov. #13
    Source code(tar.gz)
    Source code(zip)
  • v0.11.1(Sep 19, 2019)

  • v0.11.0(Aug 30, 2019)

    Changes

    • Moved PrintActionMiddleware to SwiftDuxExtras
    • StoreProvider view modifier updates its view when a StoreAction<_> Is dispatched.

    Added

    • SwiftDuxExtras module
    • Persistence API to SwiftDuxExtras
    • PersistStateMiddleware
    • StoreReducer for store specific actions.
    • "StoreAction<_>.prepare" action fired from the store at initialization.
    • "StoreAction<_>.reset(state:)" action to replace the current state of the store.
    Source code(tar.gz)
    Source code(zip)
  • v0.10.0(Aug 30, 2019)

    Changes

    • PublishableActionPlan has been merged into ActionPlan.
    • All actions dispatched from a view will update that view. This includes actions dispatched inside an action plan.

    Added

    • Middleware API
    • PrintActionMiddleware
    Source code(tar.gz)
    Source code(zip)
  • v0.9.0(Aug 26, 2019)

    Changes

    • The view of a mapped state will wait to render until the state is exists (non-nil). This completes all the previous functionality of the Connector API.
    • The updateWhen argument of View.connect(updateWhen:mapState:) is now optional with a default value.

    Fixed

    • Views continue to properly update when dispatching an acton after their parent view has re-rendered.

    Removed

    • Connector API has been removed.
    Source code(tar.gz)
    Source code(zip)
  • v0.8.0(Aug 2, 2019)

    Changes

    • Reimplemented property wrapper API now that environment objects are working correctly.
    • Renamed the modifyAction(_:) method to onAction(perform:) to better match the SwiftUI API.
    • An ActionDispatcher no longer returns a publisher when sending an action to simply the API.

    Added

    • Connectable and ParameterizedConnectable protocols to replace the Connector API.
    • @MappedState property wrapper to bind a mapped state to a view.
    • @MappedDispatch property wrapper to bind an action dispatcher to a view.

    Deprecated

    • Connector API in favor of Connectable
    Source code(tar.gz)
    Source code(zip)
  • v0.7.1(Jul 20, 2019)

  • v0.7.0(Jul 6, 2019)

    This version reverts from using property wrappers back to a simplified API due to SwiftUI's inability to handle the creation of bindable objects. It appears that bindable objects of any kind have to be external and/or singleton in nature within SwiftUI. The new Connector API is built around this.

    Changes

    • Updated to support beta 3.
    • Removed property wrappers with a new Connector API to externalize and reduce the creation of bindable objects within SwiftUI.
    • Removed withState view modifier in favor of Connector API.
    • Added a new Connector API to generate the mapping of state to a view outside of SwiftUI.
    • Removed modifyAction in favor of using DispatcherProxy directly, so that it can be created outside of SwiftUI.

    Known issues

    • NavigationLink has a bug with environment objects. See the "Known Issues" section of the README.
    Source code(tar.gz)
    Source code(zip)
  • v0.6.0(Jun 22, 2019)

    Additions

    • Added onAppearAsync() and onDisappearAsync() to View to help trigger view updates when dispatching actions.

    Changes

    • OrderedState<_> is now a RandomAccessCollection, allowing it to be used directly with SwiftUI's list elements.
    • OrderedState<_> now allows subscripting by index or id.

    Fixes

    • The state mapping API now assumes SwiftUI Views may still exist shortly after the state itself was removed.
    Source code(tar.gz)
    Source code(zip)
  • v0.5.0(Jun 20, 2019)

    Changes

    • Made major refinements to the property wrapper API.
    • New state mapping API to map the application to a substate, views can focus on just their slice.
    • Store publishes changes on main thread.
    Source code(tar.gz)
    Source code(zip)
  • v0.4.0(Jun 20, 2019)

    Changes

    • Changed action plans from a closure type to structs.
    • ActionDispatcher is simplified to have one method that can received all types of actions (including action plans)
    • Converted API to use the new property wrapper API of Swift when mapping state to a view.
    Source code(tar.gz)
    Source code(zip)
  • v0.3.0(Jun 16, 2019)

    Changes

    • Added Store<>.connect(:, wrapper:) to update views using a mapping of the application's state as an alternative to updating off of an action type.
    • Hooked up dispatcher proxying for connect functions.
    Source code(tar.gz)
    Source code(zip)
  • v0.2.0(Jun 15, 2019)

    Changes

    • Added a new StoreActionDispatcher to separate the store from action dispatching. This allows the ability to proxy, modify, and monitor actions sent upstream.
    • Added View.provideStore(_:) modifier to inject an initial store and dispatcher object into the view environment.
    • Cleaned up and refined APIs
    • Added and cleaned up documentation
    • Reducer.reduceAny(state:action:) will always dispatch the action to Reducer.reduceNext(state:action:)
    Source code(tar.gz)
    Source code(zip)
A New, Modern Reactive State Management Library for Swift and SwiftUI (The iOS implementation of Recoil)

RecoilSwift RecoilSwift is a lightweight & reactive swift state management library. RecoilSwift is a SwiftUI implementation of recoil.js which powered

Holly Li 160 Dec 25, 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
VueFlux is the architecture to manage state with unidirectional data flow 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
A library for reactive and unidirectional Swift applications

ReactorKit is a framework for a reactive and unidirectional Swift application architecture. This repository introduces the basic concept of ReactorKit

ReactorKit 2.5k Dec 28, 2022
EventBroadcaster is a lightweight event handler framework, written in swift for iOS, macOS, tvOS & watchOS applications.

EventBroadcaster is a lightweight event handler framework, written in swift for iOS, macOS, tvOS & watchOS applications.

Ali Samaiee 4 Oct 5, 2022
SwiftUI-compatible framework for building browser apps with WebAssembly and native apps for other platforms

SwiftUI-compatible framework for building browser apps with WebAssembly At the moment Tokamak implements a very basic subset of SwiftUI. Its DOM rende

TokamakUI 2k Dec 30, 2022
SwiftUI-compatible framework for building browser apps with WebAssembly and native apps for other platforms

SwiftUI-compatible framework for building browser apps with WebAssembly At the moment Tokamak implements a very basic subset of SwiftUI. Its DOM rende

TokamakUI 2k Dec 29, 2022
A simple and predictable state management library inspired by Flux + Elm + Redux.

A simple and predictable state management library inspired by Flux + Elm + Redux. Flywheel is built on top of Corotuines using the concepts of structured concurrency. At the core, lies the State Machine which is based on actor model.

Abhi Muktheeswarar 35 Dec 29, 2022
Predictable state container for Swift too

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

Lucas Sunsi Abreu 38 Oct 6, 2020
Redux for Swift - a predictable state container for Swift apps

Merge / deprecation announcement: ReduxKit and Swift-Flow have joined forces! The result is ReSwift. The nitty gritty: We decided to deprecate ReduxKi

null 613 Jan 3, 2023
LRUCache is an open-source replacement for NSCache that behaves in a predictable, debuggable way

Introduction Installation Usage Concurrency Performance Credits Introduction LRUCache is an open-source replacement for NSCache that behaves in a pred

Nick Lockwood 290 Oct 20, 2022
🟣 Verge is a very tunable state-management engine on iOS App (UIKit / SwiftUI) and built-in ORM.

Verge is giving the power of state-management in muukii/Brightroom v2 development! Verge.swift ?? An effective state management architecture for iOS -

VergeGroup 478 Dec 29, 2022
Demo app for SwiftUI state management

StateObject vs ObservedObject Demo Demo app for SwiftUI state management Run the app and push 3 buttons to increase each counter. Toggle one of the to

Serhii Kyrylenko 0 Oct 13, 2021
A New, Modern Reactive State Management Library for Swift and SwiftUI (The iOS implementation of Recoil)

RecoilSwift RecoilSwift is a lightweight & reactive swift state management library. RecoilSwift is a SwiftUI implementation of recoil.js which powered

Holly Li 160 Dec 25, 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
A declarative state management and dependency injection library for SwiftUI x Concurrency

A declarative state management and dependency injection library for SwiftUI x Concurrency

Ryo Aoyama 199 Jan 1, 2023
This is a mastodon sample SwiftUI app implemented with the architecture of state management with normalized cache.

MastodonNormalizedCacheSample This is a mastodon sample SwiftUI app. This app is implemented with the architecture of state management with Normalized

null 5 Nov 27, 2022
🎭 Swift async/await & Actor-powered effectful state-management framework.

?? Actomaton ??‍?? Actor + ?? Automaton = ?? Actomaton Actomaton is Swift async/await & Actor-powered effectful state-management framework inspired by

Yasuhiro Inami 199 Dec 20, 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
Sovran-Swift: Small, efficient, easy. State Management for Swift

Sovran-Swift: Small, efficient, easy. State Management for Swift

Segment 5 Jan 3, 2023