Store - mbrandonw/swift-composable-architecture GitHub Wiki
A store represents the runtime that powers the application. It is the object that you will pass around to views that need to interact with the application.
public final class Store<State, Action>
You will typically construct a single one of these at the root of your application, and then use
the scope
method to derive more focused stores that can be passed to subviews.
Initializes a store from an initial state, a reducer, and an environment.
public convenience init<Environment>(initialState: State, reducer: Reducer<State, Action, Environment>, environment: Environment)
- initialState: - initialState: The state to start the application in.
- reducer: - reducer: The reducer that powers the business logic of the application.
- environment: - environment: The environment of dependencies for the application.
private init(initialState: State, reducer: @escaping (inout State, Action) -> Effect<Action, Never>)
var state: CurrentValueSubject<State, Never>
var effectCancellables: [UUID: AnyCancellable]
var isSending
var parentCancellable: AnyCancellable?
let reducer: (inout State, Action) -> Effect<Action, Never>
var synchronousActionsToSend: [Action]
Returns a "stateless" store by erasing state to Void
.
var stateless: Store<Void, Action>
Returns an "actionless" store by erasing action to Never
.
var actionless: Store<State, Never>
Scopes the store to one that exposes local state and actions.
public func scope<LocalState, LocalAction>(state toLocalState: @escaping (State) -> LocalState, action fromLocalAction: @escaping (LocalAction) -> Action) -> Store<LocalState, LocalAction>
This can be useful for deriving new stores to hand to child views in an application. For example:
// Application state made from local states.
struct AppState { var login: LoginState, ... }
struct AppAction { case login(LoginAction), ... }
// A store that runs the entire application.
let store = Store(initialState: AppState(), reducer: appReducer, environment: ())
// Construct a login view by scoping the store to one that works with only login domain.
let loginView = LoginView(
store: store.scope(
state: { $0.login },
action: { AppAction.login($0) }
)
)
- toLocalState: - toLocalState: A function that transforms
State
intoLocalState
. - fromLocalAction: - fromLocalAction: A function that transforms
LocalAction
intoAction
.
A new store with its domain (state and action) transformed.
Scopes the store to one that exposes local state.
public func scope<LocalState>(state toLocalState: @escaping (State) -> LocalState) -> Store<LocalState, Action>
- toLocalState: - toLocalState: A function that transforms
State
intoLocalState
.
A new store with its domain (state and action) transformed.
Scopes the store to a publisher of stores of more local state and local actions.
public func scope<P: Publisher, LocalState, LocalAction>(state toLocalState: @escaping (AnyPublisher<State, Never>) -> P, action fromLocalAction: @escaping (LocalAction) -> Action) -> AnyPublisher<Store<LocalState, LocalAction>, Never> where P.Output == LocalState, P.Failure == Never
- toLocalState: - toLocalState: A function that transforms a publisher of
State
into a publisher ofLocalState
. - fromLocalAction: - fromLocalAction: A function that transforms
LocalAction
intoAction
.
A publisher of stores with its domain (state and action) transformed.
Scopes the store to a publisher of stores of more local state and local actions.
public func scope<P: Publisher, LocalState>(state toLocalState: @escaping (AnyPublisher<State, Never>) -> P) -> AnyPublisher<Store<LocalState, Action>, Never> where P.Output == LocalState, P.Failure == Never
- toLocalState: - toLocalState: A function that transforms a publisher of
State
into a publisher ofLocalState
.
A publisher of stores with its domain (state and action) transformed.
func send(_ action: Action)
Subscribes to updates when a store containing optional state goes from nil
to non-nil
or
non-nil
to nil
.
public func ifLet<Wrapped>(then unwrap: @escaping (Store<Wrapped, Action>) -> Void, else: @escaping () -> Void) -> Cancellable where State == Wrapped?
This is useful for handling navigation in UIKit. The state for a screen that you want to
navigate to can be held as an optional value in the parent, and when that value switches
from nil
to non-nil
you want to trigger a navigation and hand the detail view a Store
whose domain has been scoped to just that feature:
class MasterViewController: UIViewController {
let store: Store<MasterState, MasterAction>
var cancellables: Set<AnyCancellable> = []
...
func viewDidLoad() {
...
self.store
.scope(state: \.optionalDetail, action: MasterAction.detail)
.ifLet(
then: { [weak self] detailStore in
self?.navigationController?.pushViewController(
DetailViewController(store: detailStore),
animated: true
)
},
else: { [weak self] in
guard let self = self else { return }
self.navigationController?.popToViewController(self, animated: true)
}
)
.store(in: &self.cancellables)
}
}
- unwrap: - unwrap: A function that is called with a store of non-optional state whenever the store's optional state goes from
nil
to non-nil
. - else: - else: A function that is called whenever the store's optional state goes from non-
nil
tonil
.
A cancellable associated with the underlying subscription.
An overload of ifLet(then:else:)
for the times that you do not want to handle the else
case.
public func ifLet<Wrapped>(then unwrap: @escaping (Store<Wrapped, Action>) -> Void) -> Cancellable where State == Wrapped?
- unwrap: - unwrap: A function that is called with a store of non-optional state whenever the store's optional state goes from
nil
to non-nil
.
A cancellable associated with the underlying subscription.