Skip to content

Instantly share code, notes, and snippets.

@mjarvis
Created March 14, 2017 16:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mjarvis/d501b49ecd231d0ca8aa1df778f12b59 to your computer and use it in GitHub Desktop.
Save mjarvis/d501b49ecd231d0ca8aa1df778f12b59 to your computer and use it in GitHub Desktop.
Generic, functional middleware
//: Playground - noun: a place where people can play
import Foundation
public protocol StateType {}
public protocol Action {}
public struct Middleware<S: StateType> {
public typealias DispatchFunction = (Action) -> Void
public typealias GetState = () -> S
internal let transform: (GetState, DispatchFunction, Action) -> [Action]
/// Create a blank slate Middleware.
public init() {
self.transform = { _, _, action in
return [action]
}
}
/**
Initialises the middleware with a transformative function.
- parameter transform: The function that will be able to modify passed actions.
*/
internal init(_ transform: @escaping (GetState, DispatchFunction, Action) -> [Action]) {
self.transform = transform
}
/// Safe encapsulation of side effects guaranteed
/// not to affect the action being passed through the middleware.
public func sideEffect(_ effect: @escaping (GetState, DispatchFunction, Action) -> Void)
-> Middleware {
return Middleware { getState, dispatch, action in
self.transform(getState, dispatch, action).map {
effect(getState, dispatch, $0)
return $0
}
}
}
/// Transform the action into another action.
public func map(_ transform: @escaping (GetState, Action) -> Action) -> Middleware {
return Middleware { getState, dispatch, action in
self.transform(getState, dispatch, action).map {
transform(getState, $0)
}
}
}
/// One to many pattern allowing one action to be turned into multiple.
public func increase(_ transform: @escaping (GetState, Action) -> [Action]) -> Middleware {
return Middleware { getState, dispatch, action in
self.transform(getState, dispatch, action).flatMap {
transform(getState, $0)
}
}
}
/// Filters while mapping actions to new actions.
public func flatMap(_ transform: @escaping (GetState, Action) -> Action?) -> Middleware {
return Middleware { getState, dispatch, action in
self.transform(getState, dispatch, action).flatMap {
transform(getState, $0)
}
}
}
/// Drop the action iff `isIncluded(action) != true`.
public func filter(_ isIncluded: @escaping (GetState, Action) -> Bool) -> Middleware {
return Middleware { getState, dispatch, action in
self.transform(getState, dispatch, action).filter {
isIncluded(getState, $0)
}
}
}
}
public class Store<S: StateType> {
let middleware: [() -> Middleware<S>]
init(middleware: [() -> Middleware<S>]) {
self.middleware = middleware
}
}
struct AppState: StateType, HasFoo, HasBar {
var foo: String = "Foo"
var bar: String = "Bar"
}
protocol HasFoo {
var foo: String { get }
}
protocol HasBar {
var bar: String { get }
}
func fooMiddleware<S: StateType & HasFoo>() -> Middleware<S> {
return Middleware<S>().sideEffect { getState, dispatch, action in
let state = getState()
print(state.foo)
}
}
func barMiddleware<S: StateType & HasBar>() -> Middleware<S> {
return Middleware<S>().sideEffect { getState, dispatch, action in
let state = getState()
print(state.bar)
}
}
let store = Store<AppState>(
middleware: [
fooMiddleware,
barMiddleware,
]
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment