MiddlewareProtocol

public protocol MiddlewareProtocol

MiddlewareProtocol is a plugin, or a composition of several plugins, that are assigned to the app global StoreType pipeline in order to handle each action received (InputActionType), to execute side-effects in response, and eventually dispatch more actions (OutputActionType) in the process. It can also access the most up-to-date StateType while handling an incoming action.

  • The Action type that this MiddlewareProtocol knows how to handle, so the store will forward actions of this type to this middleware.

    Most of the times middlewares don’t need to handle all possible actions from the whole global action tree, so we can decide to allow it to focus only on a subset of the action.

    In this case, this action type can be a subset to be lifted to a global action type in order to compose with other middlewares acting on the global action of an app. Please check doc:Lifting for more details.

    Declaration

    Swift

    associatedtype InputActionType
  • The Action type that this MiddlewareProtocol will eventually trigger back to the store in response of side-effects. This can be the same as InputActionType or different, in case you want to separate your enum in requests and responses.

    Most of the times middlewares don’t need to dispatch all possible actions of the whole global action tree, so we can decide to allow it to dispatch only a subset of the action, or not dispatch any action at all, so the OutputActionType can safely be set to Never.

    In this case, this action type can be a subset to be lifted to a global action type in order to compose with other middlewares acting on the global action of an app. Please check doc:Lifting for more details.

    Declaration

    Swift

    associatedtype OutputActionType
  • The State part that this MiddlewareProtocol needs to read in order to make decisions. This middleware will be able to read the most up-to-date StateType from the store while handling an incoming action, but it can never write or make changes to it.

    Most of the times middlewares don’t need reading the whole global state, so we can decide to allow it to read only a subset of the state, or maybe this middleware doesn’t need to read any state, so the StateType can safely be set to Void.

    In this case, this state type can be a subset to be lifted to a global state in order to compose with other middlewares acting on the global state of an app. Please check doc:Lifting for more details.

    Declaration

    Swift

    associatedtype StateType
  • Handles the incoming actions and may or not start async tasks, check the latest state at any point or dispatch additional actions.

    This is a good place for side-effects such as async tasks, timers, web, database, file access, background execution, access device sensors, perform analytics, tracking, logging and telemetry. You can schedule tasks to run after the reducer changed the global state, this will happen in the IO closure you must return from this function.

    In case no side-effect is required for certain action, returning IO/pure() should suffice.

    You can only dispatch new actions to the store from inside the IO closure.

    IMPORTANT: this will be called on the main queue, never perform expensive work on it. You should perform side-effects only in the IO block and care about running things in background. You don’t have to return to the main queue to dispatch actions, however, the store will take care of that.

    Declaration

    Swift

    func handle(action: InputActionType, from dispatcher: ActionSource, state: @escaping GetState<StateType>) -> IO<OutputActionType>

    Parameters

    action

    the incoming action to be handled

    dispatcher

    information about the action source, representing the entity that created and dispatched the action

    state

    a closure that, once called, will return the most up-to-date state. In the scope of this function, the state wasn’t handled by reducers yet, but in the context of the IO block you should expect the state to be changed already.

    Return Value

    an IO closure where you can run side-effects and dispatch new actions to the store

  • receiveContext(getState:output:) Default implementation

    Middleware setup. This function is deprecated and should never be used.

    Default Implementation

    Declaration

    Swift

    @available(*, deprecated, message: "Instead of relying on receiveContext, please use the getState from handle(action﹚ function,\nand when returning IO from the same handle(action﹚ function use the output from the closure")
    func receiveContext(getState: @escaping GetState<StateType>, output: AnyActionHandler<OutputActionType>)

    Parameters

    getState

    a closure that allows the middleware to read the current state at any point in time

    output

    an action handler that allows the middleware to dispatch new actions at any point in time

  • eraseToAnyMiddleware() Extension method

    Undocumented

    Declaration

    Swift

    public func eraseToAnyMiddleware() -> AnyMiddleware<InputActionType, OutputActionType, StateType>
  • A method used to transform a middleware focused in a specific substate into a middleware that can be plugged in a global scope and composed with other middlewares that work on different generic parameters. The global state of your app is Whole, and the Middleware handles Part, that is a sub-state. So for example you may want to have a GPSMiddleware that knows about the following struct:

    struct Location {
       let latitude: Double
       let longitude: Double
    }
    

    Let’s call it Part. Both, this state and its middleware will be part of an external framework, used by dozens of apps. Internally probably the Middleware will use CoreLocation to fetch the GPS changes, and triggers some actions. On the main app we have a global state, that we now call Whole.

    struct MyGlobalState {
       let title: String?
       let listOfItems: [Item]
       let currentLocation: Location
    }
    

    As expected, Part is a property of Whole, maybe not directly, it could be several nodes deep in the tree.

    Because our Store understands Whole and our GPSMiddleware understands Part, we must lift(_:) the middleware to the Whole level, by using:

    let globalStateMiddleware = gpsMiddleware.lift(state: \MyGlobalState.currentLocation)
    

    Now this middleware can be used within our Store or even composed with others. It also can be used in other apps as long as we have a way to lift it to the world of Whole.

    Declaration

    Swift

    public func lift<GlobalInputActionType, GlobalOutputActionType, GlobalStateType>(
        inputAction inputActionMap: @escaping (GlobalInputActionType) -> InputActionType?,
        outputAction outputActionMap: @escaping (OutputActionType) -> GlobalOutputActionType,
        state stateMap: @escaping (GlobalStateType) -> StateType
    ) -> LiftMiddleware<GlobalInputActionType, GlobalOutputActionType, GlobalStateType, Self>

    Parameters

    inputActionMap

    a function that will be executed every time a global action arrives at the global store. Then you can optionally return an action of type Middleware’s local input action type so the middleware will handle this action, or you can return nil in case you want this middleware to ignore this global action. This is useful because not all middlewares will care about all global actions. Usually this is a KeyPath in an enum, such as \GlobalAction.someSubAction?.middlewareLocalAction when you use code generators to create enum properties.

    outputActionMap

    a function that will translate the local actions dispatched by this middleware into a global action type for your store. Usually this is wrapping the enum in a global action tree, such as { GlobalAction.someSubAction(.middlewareLocalAction($0)) }.

    stateMap

    a function that will translate the global state of your store into the local state of this middleware. Usually this is a KeyPath in the global state struct, such as \GlobalState.subState.middlewareLocalState.

    Return Value

    a LiftMiddleware that knows how to translate Whole to Part and vice-versa. To the external world this resulting middleware will “speak” global types to be plugged into the main Store. Internally it will “speak” the types of the wrapped middleware.

  • lift(outputAction:state:) Extension method

    A method used to transform a middleware focused in a specific substate into a middleware that can be plugged in a global scope and composed with other middlewares that work on different generic parameters. The global state of your app is Whole, and the Middleware handles Part, that is a sub-state. So for example you may want to have a GPSMiddleware that knows about the following struct:

    struct Location {
       let latitude: Double
       let longitude: Double
    }
    

    Let’s call it Part. Both, this state and its middleware will be part of an external framework, used by dozens of apps. Internally probably the Middleware will use CoreLocation to fetch the GPS changes, and triggers some actions. On the main app we have a global state, that we now call Whole.

    struct MyGlobalState {
       let title: String?
       let listOfItems: [Item]
       let currentLocation: Location
    }
    

    As expected, Part is a property of Whole, maybe not directly, it could be several nodes deep in the tree.

    Because our Store understands Whole and our GPSMiddleware understands Part, we must lift(_:) the middleware to the Whole level, by using:

    let globalStateMiddleware = gpsMiddleware.lift(state: \MyGlobalState.currentLocation)
    

    Now this middleware can be used within our Store or even composed with others. It also can be used in other apps as long as we have a way to lift it to the world of Whole.

    Declaration

    Swift

    public func lift<GlobalInputActionType, GlobalOutputActionType, GlobalStateType>(
        outputAction outputActionMap: @escaping (OutputActionType) -> GlobalOutputActionType,
        state stateMap: @escaping (GlobalStateType) -> StateType
    ) -> LiftMiddleware<GlobalInputActionType, GlobalOutputActionType, GlobalStateType, Self>
    where GlobalInputActionType == InputActionType

    Parameters

    outputActionMap

    a function that will translate the local actions dispatched by this middleware into a global action type for your store. Usually this is wrapping the enum in a global action tree, such as { GlobalAction.someSubAction(.middlewareLocalAction($0)) }.

    stateMap

    a function that will translate the global state of your store into the local state of this middleware. Usually this is a KeyPath in the global state struct, such as \GlobalState.subState.middlewareLocalState.

    Return Value

    a LiftMiddleware that knows how to translate Whole to Part and vice-versa. To the external world this resulting middleware will “speak” global types to be plugged into the main Store. Internally it will “speak” the types of the wrapped middleware.

  • lift(inputAction:state:) Extension method

    A method used to transform a middleware focused in a specific substate into a middleware that can be plugged in a global scope and composed with other middlewares that work on different generic parameters. The global state of your app is Whole, and the Middleware handles Part, that is a sub-state. So for example you may want to have a GPSMiddleware that knows about the following struct:

    struct Location {
       let latitude: Double
       let longitude: Double
    }
    

    Let’s call it Part. Both, this state and its middleware will be part of an external framework, used by dozens of apps. Internally probably the Middleware will use CoreLocation to fetch the GPS changes, and triggers some actions. On the main app we have a global state, that we now call Whole.

    struct MyGlobalState {
       let title: String?
       let listOfItems: [Item]
       let currentLocation: Location
    }
    

    As expected, Part is a property of Whole, maybe not directly, it could be several nodes deep in the tree.

    Because our Store understands Whole and our GPSMiddleware understands Part, we must lift(_:) the middleware to the Whole level, by using:

    let globalStateMiddleware = gpsMiddleware.lift(state: \MyGlobalState.currentLocation)
    

    Now this middleware can be used within our Store or even composed with others. It also can be used in other apps as long as we have a way to lift it to the world of Whole.

    Declaration

    Swift

    public func lift<GlobalInputActionType, GlobalOutputActionType, GlobalStateType>(
        inputAction inputActionMap: @escaping (GlobalInputActionType) -> InputActionType?,
        state stateMap: @escaping (GlobalStateType) -> StateType
    ) -> LiftMiddleware<GlobalInputActionType, GlobalOutputActionType, GlobalStateType, Self>
    where OutputActionType == GlobalOutputActionType

    Parameters

    inputActionMap

    a function that will be executed every time a global action arrives at the global store. Then you can optionally return an action of type Middleware’s local input action type so the middleware will handle this action, or you can return nil in case you want this middleware to ignore this global action. This is useful because not all middlewares will care about all global actions. Usually this is a KeyPath in an enum, such as \GlobalAction.someSubAction?.middlewareLocalAction when you use code generators to create enum properties.

    stateMap

    a function that will translate the global state of your store into the local state of this middleware. Usually this is a KeyPath in the global state struct, such as \GlobalState.subState.middlewareLocalState.

    Return Value

    a LiftMiddleware that knows how to translate Whole to Part and vice-versa. To the external world this resulting middleware will “speak” global types to be plugged into the main Store. Internally it will “speak” the types of the wrapped middleware.

  • A method used to transform a middleware focused in a specific substate into a middleware that can be plugged in a global scope and composed with other middlewares that work on different generic parameters. The global state of your app is Whole, and the Middleware handles Part, that is a sub-state. So for example you may want to have a GPSMiddleware that knows about the following struct:

    struct Location {
       let latitude: Double
       let longitude: Double
    }
    

    Let’s call it Part. Both, this state and its middleware will be part of an external framework, used by dozens of apps. Internally probably the Middleware will use CoreLocation to fetch the GPS changes, and triggers some actions. On the main app we have a global state, that we now call Whole.

    struct MyGlobalState {
       let title: String?
       let listOfItems: [Item]
       let currentLocation: Location
    }
    

    As expected, Part is a property of Whole, maybe not directly, it could be several nodes deep in the tree.

    Because our Store understands Whole and our GPSMiddleware understands Part, we must lift(_:) the middleware to the Whole level, by using:

    let globalStateMiddleware = gpsMiddleware.lift(state: \MyGlobalState.currentLocation)
    

    Now this middleware can be used within our Store or even composed with others. It also can be used in other apps as long as we have a way to lift it to the world of Whole.

    Declaration

    Swift

    public func lift<GlobalInputActionType, GlobalOutputActionType, GlobalStateType>(
        inputAction inputActionMap: @escaping (GlobalInputActionType) -> InputActionType?,
        outputAction outputActionMap: @escaping (OutputActionType) -> GlobalOutputActionType
    ) -> LiftMiddleware<GlobalInputActionType, GlobalOutputActionType, GlobalStateType, Self>
    where GlobalStateType == StateType

    Parameters

    inputActionMap

    a function that will be executed every time a global action arrives at the global store. Then you can optionally return an action of type Middleware’s local input action type so the middleware will handle this action, or you can return nil in case you want this middleware to ignore this global action. This is useful because not all middlewares will care about all global actions. Usually this is a KeyPath in an enum, such as \GlobalAction.someSubAction?.middlewareLocalAction when you use code generators to create enum properties.

    outputActionMap

    a function that will translate the local actions dispatched by this middleware into a global action type for your store. Usually this is wrapping the enum in a global action tree, such as { GlobalAction.someSubAction(.middlewareLocalAction($0)) }.

    Return Value

    a LiftMiddleware that knows how to translate Whole to Part and vice-versa. To the external world this resulting middleware will “speak” global types to be plugged into the main Store. Internally it will “speak” the types of the wrapped middleware.

  • lift(inputAction:) Extension method

    A method used to transform a middleware focused in a specific substate into a middleware that can be plugged in a global scope and composed with other middlewares that work on different generic parameters. The global state of your app is Whole, and the Middleware handles Part, that is a sub-state. So for example you may want to have a GPSMiddleware that knows about the following struct:

    struct Location {
       let latitude: Double
       let longitude: Double
    }
    

    Let’s call it Part. Both, this state and its middleware will be part of an external framework, used by dozens of apps. Internally probably the Middleware will use CoreLocation to fetch the GPS changes, and triggers some actions. On the main app we have a global state, that we now call Whole.

    struct MyGlobalState {
       let title: String?
       let listOfItems: [Item]
       let currentLocation: Location
    }
    

    As expected, Part is a property of Whole, maybe not directly, it could be several nodes deep in the tree.

    Because our Store understands Whole and our GPSMiddleware understands Part, we must lift(_:) the middleware to the Whole level, by using:

    let globalStateMiddleware = gpsMiddleware.lift(state: \MyGlobalState.currentLocation)
    

    Now this middleware can be used within our Store or even composed with others. It also can be used in other apps as long as we have a way to lift it to the world of Whole.

    Declaration

    Swift

    public func lift<GlobalInputActionType, GlobalOutputActionType, GlobalStateType>(
        inputAction inputActionMap: @escaping (GlobalInputActionType) -> InputActionType?
    ) -> LiftMiddleware<GlobalInputActionType, GlobalOutputActionType, GlobalStateType, Self>
    where OutputActionType == GlobalOutputActionType, GlobalStateType == StateType

    Parameters

    inputActionMap

    a function that will be executed every time a global action arrives at the global store. Then you can optionally return an action of type Middleware’s local input action type so the middleware will handle this action, or you can return nil in case you want this middleware to ignore this global action. This is useful because not all middlewares will care about all global actions. Usually this is a KeyPath in an enum, such as \GlobalAction.someSubAction?.middlewareLocalAction when you use code generators to create enum properties.

    Return Value

    a LiftMiddleware that knows how to translate Whole to Part and vice-versa. To the external world this resulting middleware will “speak” global types to be plugged into the main Store. Internally it will “speak” the types of the wrapped middleware.

  • lift(outputAction:) Extension method

    A method used to transform a middleware focused in a specific substate into a middleware that can be plugged in a global scope and composed with other middlewares that work on different generic parameters. The global state of your app is Whole, and the Middleware handles Part, that is a sub-state. So for example you may want to have a GPSMiddleware that knows about the following struct:

    struct Location {
       let latitude: Double
       let longitude: Double
    }
    

    Let’s call it Part. Both, this state and its middleware will be part of an external framework, used by dozens of apps. Internally probably the Middleware will use CoreLocation to fetch the GPS changes, and triggers some actions. On the main app we have a global state, that we now call Whole.

    struct MyGlobalState {
       let title: String?
       let listOfItems: [Item]
       let currentLocation: Location
    }
    

    As expected, Part is a property of Whole, maybe not directly, it could be several nodes deep in the tree.

    Because our Store understands Whole and our GPSMiddleware understands Part, we must lift(_:) the middleware to the Whole level, by using:

    let globalStateMiddleware = gpsMiddleware.lift(state: \MyGlobalState.currentLocation)
    

    Now this middleware can be used within our Store or even composed with others. It also can be used in other apps as long as we have a way to lift it to the world of Whole.

    Declaration

    Swift

    public func lift<GlobalInputActionType, GlobalOutputActionType, GlobalStateType>(
        outputAction outputActionMap: @escaping (OutputActionType) -> GlobalOutputActionType
    ) -> LiftMiddleware<GlobalInputActionType, GlobalOutputActionType, GlobalStateType, Self>
    where GlobalInputActionType == InputActionType, GlobalStateType == StateType

    Parameters

    outputActionMap

    a function that will translate the local actions dispatched by this middleware into a global action type for your store. Usually this is wrapping the enum in a global action tree, such as { GlobalAction.someSubAction(.middlewareLocalAction($0)) }.

    Return Value

    a LiftMiddleware that knows how to translate Whole to Part and vice-versa. To the external world this resulting middleware will “speak” global types to be plugged into the main Store. Internally it will “speak” the types of the wrapped middleware.

  • lift(state:) Extension method

    A method used to transform a middleware focused in a specific substate into a middleware that can be plugged in a global scope and composed with other middlewares that work on different generic parameters. The global state of your app is Whole, and the Middleware handles Part, that is a sub-state. So for example you may want to have a GPSMiddleware that knows about the following struct:

    struct Location {
       let latitude: Double
       let longitude: Double
    }
    

    Let’s call it Part. Both, this state and its middleware will be part of an external framework, used by dozens of apps. Internally probably the Middleware will use CoreLocation to fetch the GPS changes, and triggers some actions. On the main app we have a global state, that we now call Whole.

    struct MyGlobalState {
       let title: String?
       let listOfItems: [Item]
       let currentLocation: Location
    }
    

    As expected, Part is a property of Whole, maybe not directly, it could be several nodes deep in the tree.

    Because our Store understands Whole and our GPSMiddleware understands Part, we must lift(_:) the middleware to the Whole level, by using:

    let globalStateMiddleware = gpsMiddleware.lift(state: \MyGlobalState.currentLocation)
    

    Now this middleware can be used within our Store or even composed with others. It also can be used in other apps as long as we have a way to lift it to the world of Whole.

    Declaration

    Swift

    public func lift<GlobalInputActionType, GlobalOutputActionType, GlobalStateType>(
        state stateMap: @escaping (GlobalStateType) -> StateType
    ) -> LiftMiddleware<GlobalInputActionType, GlobalOutputActionType, GlobalStateType, Self>
    where GlobalInputActionType == InputActionType, OutputActionType == GlobalOutputActionType

    Parameters

    stateMap

    a function that will translate the global state of your store into the local state of this middleware. Usually this is a KeyPath in the global state struct, such as \GlobalState.subState.middlewareLocalState.

    Return Value

    a LiftMiddleware that knows how to translate Whole to Part and vice-versa. To the external world this resulting middleware will “speak” global types to be plugged into the main Store. Internally it will “speak” the types of the wrapped middleware.

Available where StateType: Identifiable

Available where StateType: Identifiable, InputActionType == OutputActionType