Skip to content

Instantly share code, notes, and snippets.

@0xLeif
Last active December 15, 2021 02:30
Show Gist options
  • Save 0xLeif/5f7aa19212159cf97994dc6babeaa3bd to your computer and use it in GitHub Desktop.
Save 0xLeif/5f7aa19212159cf97994dc6babeaa3bd to your computer and use it in GitHub Desktop.
An example of the basic core functionality behind Reducer and Store. (Not including pullback or higher order reducers)
// MARK: - Reducable
protocol Reducable {
associatedtype State
associatedtype Action
associatedtype Environment
associatedtype Effect
func reduce(
state: inout State,
action: Action,
environment: Environment
) -> Effect?
}
// MARK: - Effects
protocol Effecting {
associatedtype Action
var work: () -> Action? { get set }
}
struct SyncClosure<Action>: Effecting {
var work: () -> Action?
init(work: @escaping () -> Action?) {
self.work = work
}
}
// MARK: - Reducer
struct Reducer<State, Action, Environment, Effect: Effecting>: Reducable where Effect.Action == Action {
let handler: (inout State, Action, Environment) -> Effect?
func reduce(
state: inout State,
action: Action,
environment: Environment
) -> Effect? {
handler(&state, action, environment)
}
}
// MARK: - Store
class Store<State, Action, Environment, Effect: Effecting> where Effect.Action == Action {
private(set) var state: State
private let reducer: Reducer<State, Action, Environment, Effect>
private let environment: Environment
init(
initialState state: State,
reducer: Reducer<State, Action, Environment, Effect>,
environment: Environment
) {
self.state = state
self.reducer = reducer
self.environment = environment
}
func send(_ action: Action) {
guard
let effect = reducer.reduce(
state: &state,
action: action,
environment: environment
)
else {
return
}
if let nextAction = effect.work() {
send(nextAction)
}
}
}
// MARK: - Example
// MARK: State
struct State {
var count: Int = 0
// Debug Values
var isAutoLogging: Bool = false
}
// MARK: Actions
enum Action {
case increment, decrement, logSelf
}
// MARK: Reducer
let reducer = Reducer<State, Action, Void, SyncClosure> { state, action, _ in
let defaultEffect: SyncClosure<Action> = { localCopy in
SyncClosure { localCopy.isAutoLogging ? Action.logSelf : nil }
}(state)
switch action {
case .increment:
state.count += 1
return defaultEffect
case .decrement:
state.count -= 1
return defaultEffect
case .logSelf:
print("Current Count: \(state.count )")
return .none
}
}
// MARK: Store
let store = Store<State, Action, Void, SyncClosure>(
initialState: State(
count: 3,
// Debug
isAutoLogging: true
),
reducer: reducer,
environment: ()
)
// MARK: Actions
store.send(.increment) // 4
store.send(.increment) // 5
store.send(.decrement) // 4
store.send(.increment) // 5
store.send(.increment) // 6
let expectedStateValue: Int = 6
assert(
store.state.count == expectedStateValue,
"The expected state value (\(expectedStateValue)) is not equal to the store's state value of \(store.state.count)"
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment