Classes
The following classes are available globally.
-
Easiest way to implement a
Middleware
, with a single function that gives you all you need, and from which you can return anEffect
.A
MiddlewareEffect
is a function providing an incomingAction
,State
andContext
(dispatcher source, dependencies, cancellation closure) and expecting as result one or multiple effects that will eventually result in outgoing actions.An
Effect
is a publisher or observable type according to your reactive framework. It can be a one-shot effect, such as an HTTP request, an observation that gives back multiple values over time, such as CoreLocation or NotificationCenter, a Timer, a pure value already known or even an empty effect using the.doNothing
constructor, if there’s no need for side-effects.Effect
cannot fail, and its element/output/value type is theOutputAction
generic of this middleware. The purpose is running tasks, creating actions as they respond and returning these actions over time back to the Store. When the Effect completes, it should send a completion event so the middleware will cleanup the resources.Cancellation: effects can optionally provide a cancellation token, which can be any hashable value. If another effect arrives in the same middleware instance having the very same cancellation token, the previous effect will be cancelled and replaced by the new one. This is useful in case you want to keep only the last request of certain kind running, but cancel any previous ongoing request when a new is dispatched. You can also explicitly cancel one or many effects at any point. For that, you will be given a
toCancel
closure during every action arrival within theContext
(third parameter). Feel free to call cancellation at that point or even later, if you hold thistoCancel
closure.Examples
Using Promises
let someMiddleware = EffectMiddleware<ApiRequestAction, ApiResponseAction, SomeState, Void>.onAction { action, state, context in switch action { case .users: return .promise { completion in DispatchQueue.main.asyncAfter(deadline: .now() + 3) { completion(ApiResponseAction.someResponse("42")) } return AnyDisposable() // Or a way to cancel the ongoing task } case .somethingIDontCare: return .doNothing } }
From Publisher
typealias ApiFetchMiddlewareDependencies = (session: @escaping () -> URLSession, decoder: @escaping () -> JSONDecoder) let apiFetchMiddleware = EffectMiddleware<ApiRequestAction, ApiResponseAction, SomeState, ApiFetchMiddlewareDependencies>.onAction { action, state, context in switch action { case .users: return context.dependencies.urlSession .dataTaskPublisher(for: fetchAllUsersURL()) .map { data, _ in data } .decode(type: [User].self, decoder: context.dependencies) .map { users in ApiResponseAction.gotUserList(users) } .replaceError(with: ApiResponseAction.errorRetrivingUserList) .asEffect case .user(id: UUID): // .. case .somethingIDontCare: return .doNothing } }.inject((session: { URLSession.shared }, decoder: JSONDecoder.init))
Cancellation
See moretypealias ApiFetchMiddlewareDependencies = (session: @escaping () -> URLSession, decoder: @escaping () -> JSONDecoder) let apiFetchMiddleware = EffectMiddleware<ApiRequestAction, ApiResponseAction, SomeState, ApiFetchMiddlewareDependencies>.onAction { action, state, context in switch action { case let .userPicture(userId): return context.dependencies.urlSession .dataTaskPublisher(for: fetchPicture()) .map { data, _ in ApiResponseAction.gotUserPicture(id: userId, data: data) } .replaceError(with: ApiResponseAction.errorRetrivingUserPicture(id: userId)) .asEffect(cancellationToken: "image-for-user-\(userId)") // this will automatically cancel any pending download for the same image // using the URL would also be possible case let .cancelImageDownload(userId): return context.toCancel("image-for-user-\(userId)") // alternatively you can explicitly cancel tasks by token } }.inject((session: { URLSession.shared }, decoder: JSONDecoder.init))
Declaration
Swift
public final class EffectMiddleware<InputActionType, OutputActionType, StateType, Dependencies> : MiddlewareProtocol
extension EffectMiddleware: Semigroup
extension EffectMiddleware: Monoid where Dependencies == Void