Created
March 14, 2017 16:01
-
-
Save mjarvis/d501b49ecd231d0ca8aa1df778f12b59 to your computer and use it in GitHub Desktop.
Generic, functional middleware
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//: 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