Prism’s architecture module implements a strict unidirectional data flow: state lives in aDocumentation Index
Fetch the complete documentation index at: https://docs.prism.byescaleira.com/llms.txt
Use this file to discover all available pages before exploring further.
PrismStore, actions flow into a PrismReducer, and asynchronous side effects are described by PrismEffect. This pattern makes state changes predictable and easy to test because the same action always produces the same state transition.
Defining state and action types
Start by declaring the state and action types for your feature. Both must conform toSendable so they can safely cross Swift concurrency boundaries. State should also conform to Equatable to enable efficient diffing.
Writing a reducer
A reducer processes one action at a time and mutates state in place. Prism supports two styles: a protocol conformance for structured types, and a closure for lightweight use.- Protocol-based
- Closure-based
Creating a store
Pass your initial state and reducer toPrismStore. The store is annotated @MainActor and @Observable, so SwiftUI views observe it automatically.
Sending actions
Callstore.send(_:) to dispatch an action synchronously. Use store.dispatch(action:) in async contexts — it wraps send for convenience.
send is @MainActor, so call it from the main actor — SwiftUI button actions and onAppear closures already satisfy this.Async side effects with PrismEffect
When an action needs to perform async work — a network call, a timer, or any other side effect — return aPrismEffect from your reducer instead of PrismEffect.none. The store runs the effect and feeds any emitted actions back through the reducer.
PrismEffect provides several factory methods:
| Method | Purpose |
|---|---|
.none | Produces no actions. Use this as the default return. |
.send(_ action:) | Emits a single action immediately. |
.sequence(_ actions:) | Emits a sequence of actions in order. |
.run(priority:_:) | Runs an async closure that calls send to emit actions. |
.merge(_ effects:) | Runs multiple effects concurrently. |
Adding middleware
Middleware runs after the reducer and is ideal for cross-cutting concerns like logging, analytics, or crash reporting. It receives the post-reduction state and the action, then returns any additional effects.Attach middleware to the store
Pass the middleware as a third argument to the store initializer. The reducer and middleware effects are merged automatically.
Using the built-in middleware chain
For middleware that needs to intercept and forward actions sequentially, usePrismMiddlewareChain with PrismChainableMiddleware conformances.
PrismLoggingMiddleware prints [PrismMiddleware] Action: … | State: … for every dispatched action. PrismThrottleMiddleware silently drops duplicate actions that arrive within the configured interval.
Scoping stores
Usestore.scope(state:action:) to derive a child store from a parent. The child stays synchronized with the parent — actions sent to the child are lifted into parent actions, and parent state changes propagate back automatically.
| Overload | When to use |
|---|---|
scope(state:action:) — closure | Full control over state projection and action lifting. |
scope(state:action:) — key path | Selects state with a key path; lifts actions with a closure. |
scope(state:) — key path only | Selects state with a key path; actions pass through unchanged. |
Cancelling effects
Callstore.cancelEffects() to immediately cancel all in-flight effect tasks managed by the store. This is useful when a view disappears and you want to stop any pending network requests.