Skip to content

Releases: subconsciousnetwork/ObservableStore

v0.6.1

19 Dec 20:29
41f3356
Compare
Choose a tag to compare

What's Changed

Full Changelog: v0.6.0...v0.6.1

v0.6.0

05 Dec 00:42
371a014
Compare
Choose a tag to compare

What's Changed

Full Changelog: v0.5.0...v0.6.0

v0.5.0

01 Dec 17:45
e648004
Compare
Choose a tag to compare

What's Changed

Full Changelog: v0.4.0...v0.5.0

v0.4.0

31 Aug 22:32
85b7322
Compare
Choose a tag to compare

What's Changed

New Contributors

Full Changelog: v0.3.0...v0.4.0

v0.3.0

27 Jan 13:20
332458e
Compare
Choose a tag to compare

What's Changed

Full Changelog: 0.2.0...v0.3.0

Breaking: removed ViewStore in favor of forward

ViewStore has been removed in favor of a new child component patter that is closer to vanilla SwiftUI.

The key problem with the ViewStore approach was that the getter for the store value dynamically got the value, meaning that for stores of list items, the state of the store always had to be an Optional. SwiftUI avoids this problem by passing down bare properties to list items. See #19 for more details.

Child components are now handled the way you handle child components in vanilla SwiftUI, with bare state and a send method.

struct ChildView: View {
    var state: ChildModel
    var send: (ChildAction) -> Void

    var body: some View {
        VStack {
            Text("Count \(state.count)")
            Button(
                "Increment",
                action: {
                    store.send(ChildAction.increment)
                }
            )
        }
    }
}

To integrate the child view, we can use Address.forward to create a tagged send function for the child that will translate all child actions into parent actions.

struct ContentView: View {
    @StateObject private var store = Store(
        state: AppModel(),
        environment: AppEnvironment()
    )

    var body: some View {
        ChildView(
            state: store.state.child,
            send: Address.forward(
                send: store.send,
                tag: AppChildCursor.tag
            )
        )
    }
}

Breaking: Binding signature change

Bindings have changed from Binding(store:get:tag) to Binding(get:send:tag:). Typical use looks like this:

TextView(
    "Placeholder",
    Binding(
        get: { store.state.text },
        send: store.send,
        tag: Action.setText
    )
)

The old signature relied on ViewStore's conformance to StoreProtocol. The new signature just requires a getter closure and a send function, so is less opinionated about the specifics of this library.

Actions publisher

Store now exposes Store.actions, a publisher which publishes all actions sent to send. This is useful for logging actions. It is also useful for integrating ObservableStore with other SwiftUI features, or for wiring together stores in codebases that use multiple stores.

Now both state and actions are published (state already has a publisher through $state), so it is easy to wired up store events to other SwiftUI features using onReceive.

Logging example:

.onReceive(store.actions) { action in
    // do logging
}

0.2.0

16 Sep 18:14
5ea1c8b
Compare
Choose a tag to compare

Version 0.2.0 is an important update to ObservableStore. It introduces the ability to create scoped stores for sub-components, called ViewStores. ViewStores are conceptually like bindings to stores, except that they expose the store API, instead of a binding API. This approach is inspired by the component mapping pattern from Elm.

Changes

  • Introduce StoreProtocol, a protocol that describes a kind of store.
  • Introduce ViewStore<Model: ModelProtocol>, which implements StoreProtocol
  • Implement StoreProtocol for Store
  • Introduce CursorProtocol, which describes how to map from one domain to another, and provides a convenience function for updating child components.
  • Introduce ModelProtocol, which implements an update(state:action:environment) static function.
    • ModelProtocol allows us to treat action and environment as associatedtypes, which simplifies many of our other type signatures.
  • Synthesize update(state:actions:environment) method for any type that implements ModelProtocol. This allows you to dispatch multiple actions immediately in sequence, effectively composing multiple actions.
    • This helper replaces pipe
    • Useful when dispatching actions down to multiple child sub-components
  • Add new tests

Breaking changes

  • Store requires that state implement ModelProtocol. This allows us to simplify the signatures of many other APIs
    • Update type signature changes from Update<State, Action> to Update<Model: ModelProtocol>.
    • Store type signature changes from Store<State, Action, Environment> to Store<Model: ModelProtocol>
    • Store initializer changes from Store.init(update:state:environment:) to Store.init(state:environment:). Now that state conforms to ModelProtocol, you don't have to explicitly pass in the update function as a closure. We can just call the protocol implementation.
  • store.binding has been removed in favor of Binding(store:get:send:)
  • Remove Update.pipe. Redundant now. Was never happy with it anyway. It was an inelegant way to accomplish the same thing as update(state:actions:environment:).
  • Join fx on main with a .default QoS. We have reduced the QoS from .userInteractive to avoid spamming that QoS with actions. This should not affect ordinary use of ObservableStore. fx are async/never block user interaction anyway, so a default QoS should be fine.

0.1.0

26 Mar 18:45
546c7d0
Compare
Choose a tag to compare

Introduces new features, bugfixes, and cleans up geneneric type arguments, function signatures, and argument labels.

Note for upgrading from 0.0.7 to 0.1.0: 0.1.0 includes breaking changes to APIs. Because this version is pre-1.0, and we are still working out APIs, these breaking changes are expressed as a minor revision (0.1.0) instead of a major revision.

Release notes:

  • Changed Store generic signature to Store<State, Action, Environment>. See #14
  • Changed update function signature to update(State, Action, Environment). See #14
  • Removed argument label from send so it is just Store.send(_). See #10
  • Changed argument label for subscribe to Store.subscribe(to:). See #10
  • Introduced .binding(get:tag:) so that constructing a binding without specifying an animation will leave transaction animation untouched. See #12
  • Added Update.mergeFx(_) which can be used to merge additional fx into an Update. Useful when composing update functions with Update.pipe. See #9
  • Removed Update.transaction method. See #9