State Management
Relevant source files
The following files were used as context for generating this wiki page:
State management in Spotty follows a unidirectional data flow architecture where the SpottyDomain.PlaybackState represents the single atomic presentation snapshot of the application's playback status. The system centralizes all mutations through a PlaybackReducer to ensure determinism and portability across the domain layer.
The architecture separates pure policy, defined in SpottyDomain, from side effects and environment interactions, which are managed in the Spotty composition shell. This separation ensures that the core logic remains testable and independent of UI, audio, or network dependencies.
Sources: Sources/Spotty/Spotify/AGENTS.md:12-15, Sources/SpottyDomain/AGENTS.md:6-10
Architecture Overview
Spotty utilizes a tiered architecture where dependencies flow from the executable launcher down to the domain policy. The state management system relies on three primary components:
- PlaybackState: A deterministic snapshot of the current playback, connection, and queue status.
- PlaybackReducer: The exclusive entry point for state mutations, processing events to produce new state snapshots.
- PlaybackStore: A
@MainActorsurface that holds the state and provides an interface for the UI to dispatch actions.
The diagram shows the unidirectional flow from the UI through the store to the pure domain reducer and back.
Sources: Sources/Spotty/AGENTS.md:12-20, Sources/Spotty/Spotify/AGENTS.md:12-15
Domain State and Reducers
The SpottyDomain layer defines the core structures that represent the application's truth. These structures are designed to be portable and free of side effects like network calls or file I/O.
PlaybackState Invariants
PlaybackState acts as the single atomic presentation snapshot. It incorporates transport state, device connection details, and queue projections.
- Connection Policy: The state maintains session-phase and empty-device-ID semantics through
ConnectionSnapshotProjection. - Transport Policy: It handles transport rehydration using
ResumeLoadPlan, which captures target order from sticky resume-load URIs rather than transient snapshots.
Sources: Sources/SpottyDomain/AGENTS.md:14-18
Reducer Logic
PlaybackReducer governs all state transitions. It enforces strict rules regarding event acceptance and lifetime:
- Atomicity: Transitions are explicit and deterministic.
- Epoch Validation: The reducer rejects stale callbacks and requests based on account and engine generations.
- Revision Tracking: Ordered sources carry revisions; the owner compares and commits revision state to prevent out-of-order updates.
Sources: Sources/Spotty/Spotify/AGENTS.md:30-32, Sources/SpottyDomain/AGENTS.md:12-14
Store Management
The PlaybackStore manages the application-level state and orchestrates asynchronous effects. It serves as the bridge between the pure domain logic and the platform-specific implementation.
Key Store Responsibilities
| Component | Responsibility |
|---|---|
PlaybackStore | Serves as the @MainActor state/action surface; prohibits second writers. |
PlaybackCoordinator | Serializes commands sent to the playback engine. |
PlaybackEffectRegistry | Owns store-level task lifetimes and manages explicit cancellation. |
TransientFeedbackPresenter | Manages transient mutation banners (e.g., success/failure feedback). |
Sources: Sources/Spotty/AGENTS.md:13-17, Sources/Spotty/Spotify/AGENTS.md:12-17
Effect Execution Flow
When an action requires a side effect (like a network request), the PlaybackEffectRegistry manages the task. Tasks are keyed and can be replaced or cancelled to prevent race conditions.
The sequence shows how the store delegates asynchronous work to the registry to maintain a responsive main thread.
Sources: Sources/Spotty/AGENTS.md:13-15, Sources/Spotty/Spotify/AGENTS.md:16-19
Catalog and Library State
State management for the user's library and home screen is handled by the HomeLibraryStore. This store manages metadata, playlists, and user profiles independently of the active playback session.
Catalog Data Flow
The HomeLibraryStore organizes data into sections (e.g., Home, Playlists, Liked Songs). It uses AccountScopedRequestIdentity to ensure that data fetched from the Spotify API remains consistent with the current authenticated user session.
- Concurrency: Requests for different sections run concurrently and publish independently.
- Validation: Before applying updates, the store validates the
requestID,accountEpoch, andsessionRevision. - Caching: Items are cached in the
CatalogMetadataRepositoryto facilitate efficient display in track tables and sidebars.
Sources: Sources/Spotty/Spotify/HomeLibraryStore.swift:42-55, Sources/Spotty/Spotify/HomeLibraryStore.swift:157-190
Loading and Error Handling
The store tracks the status of each section to provide honest feedback to the UI.
| State Property | Description |
|---|---|
loadingSections | A set containing sections currently performing network requests. |
loadedSections | A set containing sections that have successfully updated in the current session. |
errors | A dictionary mapping sections to localized error messages. |
Sources: Sources/Spotty/Spotify/HomeLibraryStore.swift:29-37
Implementation Constraints
To maintain state integrity, the following architectural constraints are strictly enforced:
- No Partial Updates: Never add second writers or partial in-place updates to the state; all changes must pass through the reducer.
- Task Ownership: Use explicit keys in
PlaybackEffectRegistryrather than untracked asynchronous work. - Isolation: Treat Swift concurrency diagnostics as correctness failures. Do not use
nonisolated(unsafe)or mutable globals to bypass isolation. - Boundary Invariants: New asynchronous boundaries must define owner, lifetime, and failure policies before implementation.
Sources: Sources/Spotty/AGENTS.md:46-50, Sources/Spotty/Spotify/AGENTS.md:12-15, Sources/Spotty/Spotify/AGENTS.md:23-26
The state management system in Spotty prioritizes native macOS behavior and "truthful state," ensuring that the interface never shows speculative playback state or actions that cannot succeed.
Sources: Sources/Spotty/Views/AGENTS.md:14-16