Protocols
The following protocols are available globally.
- 
                  
                  
See moreMiddlewareProtocolis a plugin, or a composition of several plugins, that are assigned to the app globalStoreTypepipeline 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-dateStateTypewhile handling an incoming action.Declaration
Swift
public protocol MiddlewareProtocol - 
                  
                  
ActionHandlerdefines a protocol for entities able to handle actions - defined by the associated typeActionType.The only protocol requirement is a function that allows other entities to dispatch actions, so Views (or Presenters, ViewModels) in your UI layer, or even Middlewares can create actions of a certain type and send to your store, that is generalized by this protocol.
See moreDeclaration
Swift
public protocol ActionHandler - 
                  
                  
StateProviderdefines a protocol for entities able to offer state publishers (Combine Publisher, RxSwift Observable, ReactiveSwift SignalProducer) of certainStateType, so everybody can observe the global state changes through this container. Usually aStorewill implement that, but it can also be aStoreProjectionwith a state that is derived from the global source-of-truth.The only protocol requirement is to offer a property
See morestatePublisherthat will allow other entities to subscribe to state changes and react to those.Declaration
Swift
public protocol StateProvider - 
                  
                  
A protocol that defines the two expected roles of a “Store”: receive/distribute actions (
See moreActionHandler); and publish changes of the the current app state (StateProvider) to possible subscribers. It can be a real store (such asReduxStoreBase) or just a “proxy” that acts on behalf of a real store, for example, in the case ofStoreProjection.Declaration
Swift
public protocol StoreType : ActionHandler, StateProvider - 
                  
                  
A protocol to generalize MiddlewareReader. Unless you look for some very special behaviour, you should use MiddlewareReader directly which provides everything needed for your Middleware dependency injection.
See moreDeclaration
Swift
public protocol MiddlewareReaderProtocol - 
                  
                  
⛓
Middlewareis a plugin, or a composition of several plugins, that are assigned to theReduxStoreProtocolpipeline in order to handle each action received (InputActionType), to execute side-effects in response, and eventually dispatch more actions (OutputActionType) in the process. This happens before theReducerto do its job.We can think of a Middleware as an object that transforms actions into sync or async tasks and create more actions as these side-effects complete, also being able to check the current state at any point.
An action is a lightweight structure, typically an enum, that is dispatched into the
ActionHandler(usually aStoreProtocol). A Store likeReduxStoreProtocolenqueues a new action that arrives and submits it to a pipeline of middlewares. So, in other words, aMiddlewareis class that handles actions, and has the power to dispatch more actions to theActionHandlerchain. TheMiddlewarecan also simply ignore the action, or it can execute side-effects in response, such as logging into file or over the network, or execute http requests, for example. In case of those async tasks, when they complete the middleware can dispatch new actions containing a payload with the response (a JSON file, an array of movies, credentials, etc). Other middlewares will handle that, or maybe even the same middleware in a future RunLoop, or perhaps someReducer, as reducers pipeline is at the end of every middleware pipeline.Middlewares can schedule a callback to be executed after the reducer pipeline is done mutating the global state. At that point, the middleware will have access to the new state, and in case it cached the old state it can compare them, log, audit, perform analytics tracking, telemetry or state sync with external devices, such as Apple Watches. Remote Debugging over the network is also a great use of a Middleware.
Every action dispatched also comes with its action source, which is the primary dispatcher of that action. Middlewares can access the file, line, function and additional information about the entity responsible for creating and dispatching that action, which is a very powerful debugging information that can help developers to trace how the information flows through the app.
Because the
Middlewarereceive all actions and accesses the state of the app at any point, anything can be done from these small and reusable boxes. For example, the sameCoreLocationmiddleware could be used from an iOS app, its extensions, the Apple Watch extension or even different apps, as long as they share some sub-state struct.Some suggestions of middlewares:
- Run Timers, pooling some external resource or updating some local state at a constant time
 - Subscribe for 
CoreData,Realm,Firebase Realtime Databaseor equivalent database changes - Be a 
CoreLocationdelegate, checking for significant location changes or beacon ranges and triggering actions to update the state - Be a 
HealthKitdelegate to track activities, or even combining that withCoreLocationobservation in order to track the activity route - Logger, Telemetry, Auditing, Analytics tracker, Crash report breadcrumbs
 - Monitoring or debugging tools, like external apps to monitor the state and actions remotely from a different device
 WatchConnectivitysync, keep iOS and watchOS state in sync- API calls and other “cold observables”
 - Network Reachability
 - Navigation through the app (Redux Coordinator pattern)
 CoreBluetoothcentral or peripheral managerCoreNFCmanager and delegateNotificationCenterand other delegates- WebSocket, TCP Socket, Multipeer and many other connectivity protocols
 RxSwiftobservables,ReactiveSwiftsignal producers,Combinepublishers- Observation of traits changes, device rotation, language/locale, dark mode, dynamic fonts, background/foreground state
 - Any side-effect, I/O, networking, sensors, third-party libraries that you want to abstract
 
┌─────┐ ┌─────┐ │ │ handle ┌──────────┐ request ┌ ─ ─ ─ ─ response ┌──────────┐ dispatch │ │ │ │ ┌─────────▶│Middleware├─────────────▶ External│─────────────▶│Middleware│───────────▶│Store│─ ─ ▶ ... │ │ │ Action │ Pipeline │ side-effects │ World side-effects │ callback │ New Action │ │ │ │ │ └──────────┘ ─ ─ ─ ─ ┘ └──────────┘ └─────┘ ┌──────┐ dispatch │ │ │ ▲ │Button│─────────▶│Store│──▶│ └───afterReducer─────┐ ┌────────┐ └──────┘ Action │ │ │ │ ┌─▶│ View 1 │ │ │ │ ┌─────┐ │ └────────┘ │ │ │ reduce ┌──────────┐ │ │ onNext │ ┌────────┐ │ │ └─────────▶│ Reducer ├───────────▶│Store│────────────▶├─▶│ View 2 │ │ │ Action │ Pipeline │ New state │ │ New state │ └────────┘ └─────┘ + └──────────┘ └─────┘ │ ┌────────┐ State └─▶│ View 3 │ └────────┘Middleware protocol is generic over 3 associated types:
InputActionType:
The Action type that this
Middlewareknows how to handle, so the store will forward actions of this type to this middleware. Thanks to optics, this action can be a sub-action lifted to a global action type in order to compose with other middlewares acting on the global action of an app. Please checklift(inputActionMap:outputActionMap:stateMap:)for more details.OutputActionType:
The Action type that this
Middlewarewill eventually trigger back to the store in response of side-effects. This can be the same asInputActionTypeor different, in case you want to separate your enum in requests and responses. Thanks to optics, this action can be a sub-action lifted to a global action type in order to compose with other middlewares acting on the global action of an app. Please checklift(inputActionMap:outputActionMap:stateMap:)for more details.StateType:
The State part that this
Middlewareneeds to read in order to make decisions. This middleware will be able to read the most up-to-dateStateTypefrom the store at any point in time, but it can never write or make changes to it. In some cases, middleware don’t need reading the whole global state, so we can decide to allow only a sub-state, or maybe this middleware doesn’t need to read any state, so theStateTypecan safely be set toVoid. Thanks to lenses, this state can be a sub-state lifted to a global state in order to compose with other middlewares acting on the global state of an app. Please checklift(inputActionMap:outputActionMap:stateMap:)for more details.When implementing your Middleware, all you have to do is to handle the incoming actions:
When implementing your Middleware, all you have to do is to handle the incoming actions:
class LoggerMiddleware: Middleware { typealias InputActionType = AppGlobalAction // It wants to receive all possible app actions typealias OutputActionType = Never // No action is generated from this Middleware typealias StateType = AppGlobalState // It wants to read the whole app state var getState: GetState<AppGlobalState>! func receiveContext(getState: @escaping GetState<AppGlobalState>, output: AnyActionHandler<Never>) { self.getState = getState } func handle(action: AppGlobalAction, from dispatcher: ActionSource, afterReducer: inout AfterReducer) { let stateBefore: AppGlobalState = getState() let dateBefore = Date() afterReducer = .do { let stateAfter = self.getState() let dateAfter = Date() let source = "\(dispatcher.file):\(dispatcher.line) - \(dispatcher.function) | \(dispatcher.info ?? "")" Logger.log(action: action, from: source, before: stateBefore, after: stateAfter, dateBefore: dateBefore, dateAfter: dateAfter) } } } class FavoritesAPIMiddleware: Middleware { typealias InputActionType = FavoritesAction // It wants to receive only actions related to Favorites typealias OutputActionType = FavoritesAction // It wants to also dispatch actions related to Favorites typealias StateType = FavoritesModel // It wants to read the app state that manages favorites var getState: GetState<FavoritesModel>! var output: AnyActionHandler<FavoritesAction>! func receiveContext(getState: @escaping GetState<FavoritesModel>, output: AnyActionHandler<FavoritesAction>) { self.getState = getState self.output = output } func handle(action: FavoritesAction, from dispatcher: ActionSource, afterReducer: inout AfterReducer) { guard let .toggleFavorite(movieId) = action else { return } let favoritesList = getState() let makeFavorite = !favoritesList.contains(where: { $0.id == movieId }) API.changeFavorite(id: movieId, makeFavorite: makeFavorite) (completion: { result in switch result { case let .success(value): self.output.dispatch(.changedFavorite(movieId, isFavorite: true), info: "API.changeFavorite callback") case let .failure(error): self.output.dispatch(.changedFavoriteHasFailed(movieId, isFavorite: false, error: error), info: "API.changeFavorite callback") } }) } }
See more
Declaration
Swift
@available(*, deprecated, message: "Use `MiddlewareProtocol` instead of `Middleware`. This protocol will be removed on 1.0.") public protocol Middleware : MiddlewareProtocol - 
                  
                  
Protocol for a semigroup, any algebraic structure that allows two of its elements to be combined into one,
(A, A) -> A, for any of its elements and keeping associativity property for all the cases, for example:(a1 <> a2) <> a3 = a1 <> (a2 <> a3)for anyas inA.Axioms:
- Totality
 - Associativity
 
For example, having a
See moref(x) -> xand ag(x) -> x, one would be able to composeh = f <> gin a way that the new functionh(x)will be similar tog(f(x))Declaration
Swift
public protocol Semigroup - 
                  
                  
Protocol for a monoid algebra, allowing monoidal composition. It’s a
Semigroupwith identity element, element which, when combined to any other element, will keep the other elemenet unchanged, regardless if the composition happened from the left or from the right, for example:a <> identity = identity <> a = a, for anyainA.Axioms:
- Totality
 - Associativity
 - Identity
 
For example, having a
See moref(x) -> xand ag(x) -> x, one would be able to composeh = f <> gin a way that the new functionh(x)will be similar tog(f(x)), and there should be a functioni(x)wherei, when composed to any other function, will not change the result:f <> i = i <> f = f, forf,g,hand all other endo-functions.Declaration
Swift
public protocol Monoid : Semigroup - 
                  
                  
Abstraction over subscription types from reactive frameworks. This abstraction uses concept similar to type-erasure or protocol witness pattern, wrapping the behaviour of concrete implementations and delegating to them once the wrapper funcions are called.
See moreDeclaration
Swift
public protocol SubscriptionType - 
                  
                  
Abstraction over subscription collection (
See moreDisposeBagorSet<AnyCancellabledepending on your chosen reactive framework), useful for keeping subscriptions alive while the parent class is alive, binding the lifecycle of subscriptions to the lifecycle of views, view controllers or presenters. Subscriptions added to a subscription collection will be cancelled/disposed automatically once the collection gets deallocated, stopping any pending operation and cleaning up the resources. This abstraction uses concept similar to type-erasure or protocol witness pattern, wrapping the behaviour of concrete implementations and delegating to them once the wrapper funcions are called.Declaration
Swift
public protocol SubscriptionCollection 
View on GitHub
        Protocols  Reference