Skip to content

Instantly share code, notes, and snippets.

@lukaszx0
Created November 10, 2019 07:49
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 lukaszx0/00d6532045b38d44014854458d541f47 to your computer and use it in GitHub Desktop.
Save lukaszx0/00d6532045b38d44014854458d541f47 to your computer and use it in GitHub Desktop.
import Foundation
import Combine
protocol ActionType {}
protocol StateType {}
struct Command<Action: ActionType> {
let execute: (SendFunction<Action>) -> Void
}
struct Reducer<State: StateType, Action: ActionType> {
let reduce: (inout State, Action) -> [Command<Action>]?
}
typealias SendFunction<Action: ActionType> = (Action) -> ()
typealias Middleware<State: StateType, Action: ActionType> = (@escaping SendFunction<Action>, @escaping () -> State?) -> (@escaping SendFunction<Action>) -> SendFunction<Action>
final class Store<State: StateType, Action: ActionType>: ObservableObject {
@Published private(set) var state: State
private(set) var isSending = false
private let reducer: Reducer<State, Action>
private var sendFunction: SendFunction<Action>!
init(initialState: State, initialAction: Action, reducer: Reducer<State, Action>, middleware: [Middleware<State, Action>] = []) {
self.state = initialState
self.reducer = reducer
self.sendFunction = buildSendFunction(middleware)
send(initialAction)
}
func send(_ action: Action) {
sendFunction(action)
}
func sendAsync(_ action: Action) {
DispatchQueue.main.async {
self.send(action)
}
}
private func internalSend(_ action: Action) {
if isSending {
fatalError("Action sent while the state is being processed")
}
// TODO: make it atomic (threadsafe)
isSending = true
let commands = reducer.reduce(&state, action) ?? []
isSending = false
for command in commands {
command.execute(sendAsync)
}
}
private func buildSendFunction(_ middleware: [Middleware<State, Action>]) -> SendFunction<Action> {
return middleware.reversed().reduce({ [unowned self] action in
return self.internalSend(action)
}, { sendFunction, middleware in
let send: SendFunction<Action> = { [weak self] in self?.sendFunction($0) }
let getState = { [weak self] in self?.state }
return middleware(send, getState)(sendFunction)
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment