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 requested by the application-specific logic. In particular, you can require the Store to execute something by dispatching a dispatchable item.

Currently the store handles 2 types of dispatchable: State Updater, Side Effect

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. 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 a complex/asynchronous logic
  • Closure that is used to initialize the dependencies

    Declaration

    Swift

    public typealias DependenciesInitializer = (_ dispatch: @escaping AnyDispatch, _ getState: @escaping StateInitializer<S>) -> D
  • Whether the store is ready to execute operations

    Declaration

    Swift

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

    See also

    SideEffect

    Declaration

    Swift

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

    Declaration

    Swift

    public convenience 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

    public convenience init(interceptors: [StoreInterceptor])

    Parameters

    interceptors

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

    Return Value

    An instance of the store

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

    Declaration

    Swift

    public convenience init(
      interceptors: [StoreInterceptor],
      stateInitializer: @escaping StateInitializer<S>,
      configuration: Configuration = .init()
    )

    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

    configuration

    the configuration needed by the store to start properly

    Return Value

    An instance of the 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 practice 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>,
      dependenciesInitializer: @escaping DependenciesInitializer,
      configuration: Configuration = .init()
    )

    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

    dependenciesInitializer

    a closure invoked to instantiate the dependencies

    configuration

    the configuration needed by the store to start properly

    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 addAnyListener(_ listener: @escaping AnyStoreListener) -> StoreUnsubscribe

    Parameters

    listener

    the listener closure

    Return Value

    a closure that can be used to remove the listener

  • Adds a typed listener to the store. A listener is basically a closure that is invoked every time the Store’s state changes

    Declaration

    Swift

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

    Parameters

    listener

    the listener closure

    Return Value

    a closure that can be used to remove the listener

  • Dispatches a ReturningSideEffect item

    Threading

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

    When a ReturningSideEffect is dispatched, Katana will handle them in a parallel queue. A ReturningSideEffect is executed and considered done when its body finishes to be executed. This means 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 greatly 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 ReturningSideEffect.

    Promise Resolution

    When it comes to ReturningSideEffects, the promise is resolved when the body of the ReturningSideEffect is executed entirely (see ReturningSideEffect documentation for more information).

    Declaration

    Swift

    @discardableResult
    override public func dispatch<T>(_ dispatchable: T) -> Promise<T.ReturnValue> where T : ReturningSideEffect

    Parameters

    dispatchable

    the side effect to dispatch

    Return Value

    a promise parameterized to SideEffect’s return value, that is resolved when the SideEffect is handled by the store

  • Dispatches a AnySideEffect item

    Threading

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

    When a AnySideEffect is dispatched, Katana will handle them in a parallel queue. A AnySideEffect is executed and considered done when its body finishes to be executed. This means 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 greatly 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 AnySideEffect.

    Promise Resolution

    When it comes to AnySideEffects, the promise is resolved when the body of the AnySideEffect is executed entirely (see AnySideEffect documentation for more information).

    Declaration

    Swift

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

    Parameters

    dispatchable

    the side effect to dispatch

    Return Value

    a promise parameterized to SideEffect’s return value, that is resolved when the SideEffect is handled by the store

  • Dispatches a Dispatchable item

    Threading

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

    When an AnyStateUpdater is dispatched, the Store enqueues it in a serial and synchronous 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 synchronous queue is never a big problem as any operation that goes in an AnyStateUpdater is very lightweight.

    Promise Resolution

    When it comes to AnyStateUpdater, the promise is resolved when the state is updated.

    Declaration

    Swift

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

    Parameters

    dispatchable

    the state updater to dispatch

    Return Value

    a promise parameterized to void that is resolved when the state updater is handled by the store

  • Dispatches a Dispatchable item

    Threading

    The Store follows strict rules about the parallelism with which dispatched items are handled. At the same 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 synchronous 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 synchronous queue is never a big problem as any operation that goes in a StateUpdater is very lightweight.

    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 means 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 greatly 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.

    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).

    Declaration

    Swift

    @discardableResult
    override public func anyDispatch(_ dispatchable: Dispatchable) -> Promise<Any>

    Parameters

    dispatchable

    the dispatchable to dispatch, it can be either a StateUpdater or a SideEffect

    Return Value

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

  • The dependencies used to initialize katana

    See more

    Declaration

    Swift

    public struct Configuration