Store
open class Store<S> : Publisher
Combine-based state management. Enables dispatching of actions, executing reducers, performing side-effects, and listening for the latest state.
Implements the Publisher
protocol, allowing direct subscription for the latest state.
import ReCombine
import Combine
struct CounterView {
struct Increment: Action {}
struct Decrement: Action {}
struct State {
var count = 0
}
static func reducer(state: State, action: Action) -> State {
var state = state
switch action {
case _ as Increment:
state.count += 1
return state
case _ as Decrement:
state.count -= 1
return state
default:
return state
}
}
static let effect = Effect(dispatch: false) { (actions: AnyPublisher<Action, Never>) in
actions.ofTypes(Increment.self, Decrement.self).print("Action Dispatched").eraseToAnyPublisher()
}
}
let store = Store(reducer: CounterView.reducer, initialState: CounterView.State(), effects: [CounterView.effect])
-
Creates a new Store.
Declaration
Parameters
reducer
a single reducer function which will handle reducing state for all actions dispatched to the store.
initialState
the initial state. This state will be used by consumers before the first action is dispatched.
effects
action based side-effects. Each
Effect
element is processed for the lifetime of theStore
instance. -
Dispatch
Action
to the Store. Calls reducer function with the passedaction
and previous state to generate a new state.Dispatching an action to the Store:
struct Increment: Action {} store.dispatch(action: Increment())
Declaration
Swift
open func dispatch(action: Action)
Parameters
action
action to call the reducer with.
-
Returns derived data from the application state based on a given selector function.
Selector functions help return view-specific data from a minimum application state.
Example: If a view needs the count of characters in a username, instead of storing both the username and the character count in state, store only the username, and use a selector to retrieve the count.
store.select({ (state: AppState) in state.username.count })
To enable reuse, abstract the closure into a separate property.
let selectUsernameCount = { (state: AppState) in state.username.count } // ... store.select(selectUsernameCount)
Declaration
Swift
public func select<V>(_ selector: @escaping (S) -> V) -> AnyPublisher<V, Never> where V : Equatable
-
Registers an effect that processes from when this function is called until the returned
AnyCancellable
instance in cancelled.This can be useful for:
- Effects that should not process for the entire lifetime of the
Store
instance. - Effects that need to capture a particular scope in it’s
source
closure.
The following SwiftUI example shows these uses:
- Processing the
showAlert
Effect
for the lifetime of theModel
only. This is done by storing the returnedAnyCancellable
instance incancellableSet
. BecausecancellableSet
is a instance ofSet<AnyCancellable>
, it will automatically callcancel()
when on each element whenModel
is deinitialized. Capturing
self
inside theshowAlert
Effect’s source closure.class Model: ObservableObject { @Published var showAlert: Bool = false private var cancellableSet: Set<AnyCancellable> = [] init(store: Store<GetPostError>) { let showAlertOnError = Effect(dispatch: false) { actions in actions.ofType(GetPostError.self) .handleEvents(receiveOutput: { [weak self] _ in self?.showAlert = true }) .eraseActionType() .eraseToAnyPublisher() } store.register(showAlertOnError) } }
Declaration
Swift
open func register(_ effect: Effect) -> AnyCancellable
Parameters
effect
action based side-effect. It is processed until the returned
AnyCancellable
instance is cancelled. - Effects that should not process for the entire lifetime of the