Swiftpack.co - Package - StevenLambion/SwiftDux

SwiftDux

Predictable state management for SwiftUI applications.

Swift Version Platform Versions License

This is still a work in progress.

Introduction

This is yet another redux inspired state management solution for swift. It's built on top of the Combine framework with hooks for SwiftUI. This library helps build applications around an elm-like archectiture using a single, centralized state container. For more information about the architecture and this library, take a look at the getting started guide.

Documentation

Visit the documentation website.

Example

Todo Example

Installation

Xcode 11

Use the new swift package manager integration to include the libary.

Swift Package Manager

Include the library as a dependencies as shown below:

import PackageDescription

let package = Package(
  dependencies: [
    .Package(url: "https://github.com/StevenLambion/SwiftDux.git", majorVersion: 0, minor: 8)
  ]
)

SwiftUI Examples

Adding the SwiftDux store to the SwiftUI environment:

var store: Store<AppState>

...

RootView().provideStore(store)

Inject the state into a view using property wrappers:

struct BookListView : View {

  @MappedState private var books: OrderedState<Book>
  @MappedDispatch() private var dispatch

  var body: some View {
    List {
      ForEach(books) { book in
        BookRow(title: book.title)
      }
      .onMove { self.dispatch(BookAction.move(from: $0, to: $1)) }
      .onDelete { self.dispatch(BookAction.delete(at: $0)) }
    }
  }
}

/// Adhere to the Connectable protocol to map a parent state to
/// one that the view requires.

extension BookListView : Connectable {

  func map(state: AppState) -> OrderedState<Book> {
    state.books
  }

}

Update a view whenever an action is dispatched to the store.

extension BookListView : Connectable {

  /// Views always update when dispatching an action, but
  /// sometimes you may want it to update every time a given
  /// action is sent to the store.

  updateWhen(action: Action) -> Bool {
    action is BookStatusAction
  }

  func map(state: AppState) -> OrderedState<Book> {
    state.books
  }

}

Reroute actions sent from child views

struct AuthorView : View {

  @MappedState private var author: Author

  var body: some View {
    BookListContainer()
      .connect(with: author.id)
      .onAction { [author] action in
        if let action = action as? BookAction {
          return AuthorAction.routeBookAction(for: author.id, action)
        }
        return action
      }
  }

}

Known issues in SwiftUI

@MappedDispatch is requiring an explicit type

You must provide parentheses at the end of @MappedDispatch to initialize it without requiring an explicit type: @MappedDispatch() var dispatch

onAppear() doesn't update the view when dispatching actions

~~The built-in onAppear method does not trigger a view update. Use the provided onAppearAsync() instead.~~

It is now working correctly, but it only seems to run on views directly attached to a view controller under the hood. Typically this is the View directly passed to a NavigationLink. The onDisappear modifier is still not working. I'm unsure if it's some kind of optimization or incomplete functionality.

TextField caret doesn't keep up with text while typing

Starting with beta 5, using an ObservableObject with a TextField causes the caret to fall behind the text changes while typing too fast. This doesn't appear to effect @State properties, but I have been able to reproduce it using a simple ObservableObject based model. I submitted a ticket.

View doesn't update after it dispatches an action

See next issue below. For some reason, the observable object for dispatched actions is ignored by SwiftUI after its parent view has re-rendered in some cases. The observable object used by the state continues to work fine. I'm currently investigating the issue. The current fix is to manually implement the updateWhen(action:) function to rerender the view.

SwiftUI doesn't properly resubscribe to bindable objects after their initial creation.

~~Create all bindable objects outside of SwiftUI before binding them. Avoid recreating the objects.~~

~~Apple says this is fixed in beta 4. A quick test project appears to confirm it.~~

This appears to be almost fixed, but I'm still seeing it occur in one instance. I have not been able to verify if it's a bug in SwiftUI or a side-effect of its re-rendering behavior. I've submitted feedback to Apple.

Github

link
Stars: 16
Help us keep the lights on

Dependencies

Used By

Total: 0

Releases

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

v0.7.1 - Jul 20, 2019

Changes

  • Updated to beta 4 of SwiftUI and Combine.

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.

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.

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.