Store

open class Store<S, D> : PartialStore<S> where S : State, D : SideEffectDependencyContainer

The Store is a sort of single entry point that handles the logic of your application. In Katana, all the various pieces of information that your application manages should be stored in a single atom, called state (see also State protocol).

The Store, however, doesn’t really implements any application specific logic: this class only manages operations that are requsted by the application-specific logic. In particular, you can require the Store to execute something by dispatching a dispatchable item.

Currently the store handles 3 types of dispatchable: State Updater, Side Effect and (deprecated) Action.

Update the state

As written before, in Katana every relevant information in the application should be stored in the Store’s state. The only way to update the state is to dispatch a StateUpdater (or the legacy Action). At this point the Store will execute the following operations:

  • it executes the interceptors
  • it creates the new state, by invoking the updateState function of the StateUpdater
  • it updates the state
  • it resolves the promise

Handle to Business Logic

Non trivial applications require to interact with external services and/or implement complex logics. The proper way to handle these is by dispatching a SideEffect. The Store will execute the following operations:

  • it executes the interceptors
  • it executes the SideEffect body
  • it resolves the promise

Listen for updates

You can attach a listener that is invoked every time the state changes by using addListener.

Intercept Dispatchable

It is possible to intercept and reshape the behaviour of the Store by using a StoreInterceptor. A StoreInterceptor is executed every time something has dispatched, and before it is actually managed by the Store. Here you implement behaviours like logging, blocking items before they’re executed and even change dynamically which dispatchable items arrive to the Store itself.

See also

StateUpdater for more information about how to implement an update of the state

See also

SideEffect for more information about how to implement an complex/asyncronous logic
  • Whether the store is ready to execute operations

    Declaration

    Swift

    public private(set) var isReady: Bool
  • The dependencies used in the actions side effects

    Declaration

    Swift

    public var dependencies: D!
  • A convenience init method. The store won’t have middleware nor dependencies for the actions side effects. The state will be created using the default init of the state

    Declaration

    Swift

    convenience public init()

    Return Value

    An instance of store

  • A convenience init method for the Store. The initial state will be created using the default init of the state type.

    Declaration

    Swift

    convenience public init(interceptors: [StoreInterceptor])

    Parameters

    interceptors

    a list of interceptors that are executed every time something is dispatched

    Return Value

    An instance of store

  • The default init method for the Store.

    Initial phases

    When the store is created, it doesn’t immediately start to handle dispatched items. Before that, in fact, the Store will (in order)

    • create the dependencies
    • create the first state version by using the given stateInitializer
    • initialise the interceptors

    Accessing the state before the Store is ready will lead to a crash of the application, as the state of the system is not well defined. You can check whether the Store is ready by leveraging the isReady property.

    A good pratice in case you have to interact with the Store (e.g., get the state) in the initial phases of your application is to dispatch a SideEffect. When dispatching something, in fact, the Store guarantees that items are managed only after that the Store is ready. Items dispatched during the initialization are suspended and resumed as soon as the Store is ready.

    Declaration

    Swift

    public init(interceptors: [StoreInterceptor], stateInitializer: @escaping StateInitializer<S>)

    Parameters

    interceptors

    a list of interceptors that are executed every time something is dispatched

    stateInitializer

    a closure invoked to define the first state’s value

    Return Value

    An instance of store

  • Adds a listener to the store. A listener is basically a closure that is invoked every time the Store’s state changes. The listener is always invoked in the main queue

    Declaration

    Swift

    override public func addListener(_ listener: @escaping StoreListener) -> StoreUnsubscribe

    Parameters

    listener

    the listener closure

    Return Value

    a closure that can be used to remove the listener

  • Dispatches a Dispatchable item

    Threading

    The Store follows strict rules about the parallelism with which dispatched items are handled. At the sime time, it tries to leverages as much as possible the modern multi-core systems that our devices offer.

    When a StateUpdater is dispatched, the Store enqueues it in a serial and syncronous queue. This means that the Store executes one update of the state at the time, following the order in which it has received them. This is done to guarantee the predictability of the changes to the state and avoid any race condition. In general, using a syncronous queue is never a big problem as any operation that goes in a StateUpdater is very lighweight.

    When it comes to SideEffect items, Katana will handle them in a parallel queue. A SideEffect is executed and considered done when its body finishes to be executed. This menas that side effects are not guaranteed to be run in isolation, and you should take into account the fact that multiple side effects can run at the same time. This decision has been taken to greately improve the performances of the system. Overall, this should not be a problem as you cannot really change the state of the system (that is, the store’s state) without dispatching a StateUpdater.

    This version of the store keeps the support for Action items. Since actions both update the state and executes side effects, they are managed in the very same, serial and syncronous, queue of the StateUpdater. You are encouraged to move away from actions as soon as possible.

    Promise Resolution

    When it comes to StateUpdater, the promise is resolved when the state is updated. For SideEffect, the promise is resolved when the body of the SideEffect is executed entirely (see SideEffect documentation for more information). For Action, finally, the promise is resolved when the state is updated and the sideEffect method is executed (note that this doesn’t mean that the side effect is effectively done, as there’s no simple mechanism to block the execution of a sideeffect in an action)

    Declaration

    Swift

    @discardableResult
    override public func dispatch(_ dispatchable: Dispatchable) -> Promise<Void>

    Parameters

    dispatchable

    the dispatchable to dispatch

    Return Value

    a promise that is resolved when the dispatchable is handled by the store