Last active
December 15, 2021 02:30
-
-
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)
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
// 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