Files
bearwave-mac/ARCHITECTURE.md
2026-06-25 09:21:42 +02:00

179 lines
6.2 KiB
Markdown

# Architecture
This repository is a native macOS 26 SwiftPM internet radio app.
The app is split into a small app target and a larger shared core target.
## Top-Level Structure
- `Package.swift`
Defines:
- `BearWaveCore`
- `BearWave`
- `BearWaveChecks`
- `Sources/BearWaveApp`
App entrypoint and scene wiring
- `Sources/BearWaveCore`
Application logic, models, services, stores, views, and resources
## Main Runtime Objects
### `BearWaveApp`
File:
- [Sources/BearWaveApp/BearWaveApp.swift](/Users/sebastian/Development/radioappmac/Sources/BearWaveApp/BearWaveApp.swift)
Responsibilities:
- create the root `AppModel`
- host the main `WindowGroup`
- host `MenuBarExtra`
- host `Settings`
- manage window sizing and mini-player mode
- define app commands such as play/pause and `Cmd+M`
This file should stay thin. It is scene setup, not business logic.
### `AppModel`
File:
- [Sources/BearWaveCore/App/AppModel.swift](/Users/sebastian/Development/radioappmac/Sources/BearWaveCore/App/AppModel.swift)
Responsibilities:
- own the major runtime subsystems
- coordinate radio, playback, settings, and artwork
- restore launch state
- route user actions like `play(_:)`, `setVolume(_:)`, and `quit()`
Owned objects:
- `SettingsStore`
- `StationLibrary`
- `PlaybackController`
- `StationArtworkLoader`
- `NowPlayingArtworkResolver`
`AppModel` is the top-level coordinator. It should not become a dumping ground for unrelated view logic.
## Domain Store
### `StationLibrary`
File:
- [Sources/BearWaveCore/Stores/StationLibrary.swift](/Users/sebastian/Development/radioappmac/Sources/BearWaveCore/Stores/StationLibrary.swift)
Responsibilities:
- load and store browse pages
- manage global search
- manage favorites
- manage recents
- manage manual stations
- manage station folders
- persist radio-related state
Important behaviors:
- search is global, not page-local
- search results live on a dedicated `Search Results` page
- search ranking is stricter than raw Radio Browser substring matching
- folders are persisted entities and not just transient UI state
## Playback and Metadata
### `PlaybackController`
File:
- [Sources/BearWaveCore/Services/PlaybackController.swift](/Users/sebastian/Development/radioappmac/Sources/BearWaveCore/Services/PlaybackController.swift)
Responsibilities:
- own the shared `AVPlayer`
- manage playback state transitions
- play radio streams
- expose explicit playback states
- read timed metadata through `AVPlayerItemMetadataOutput`
- update `MPNowPlayingInfoCenter`
- handle artwork updates
Important invariants:
- do not collapse playback state to only `isPlaying`
- metadata handling must tolerate streams that provide nothing useful
## Network Services
### `RadioBrowserClient`
File:
- [Sources/BearWaveCore/Services/RadioBrowserClient.swift](/Users/sebastian/Development/radioappmac/Sources/BearWaveCore/Services/RadioBrowserClient.swift)
Responsibilities:
- call Radio Browser endpoints
- decode station payloads
- provide search support
- cache API responses on disk
### Artwork Services
Files:
- [Sources/BearWaveCore/Services/StationArtworkLoader.swift](/Users/sebastian/Development/radioappmac/Sources/BearWaveCore/Services/StationArtworkLoader.swift)
- [Sources/BearWaveCore/Services/NowPlayingArtworkResolver.swift](/Users/sebastian/Development/radioappmac/Sources/BearWaveCore/Services/NowPlayingArtworkResolver.swift)
Responsibilities:
- load and cache station favicons
- resolve playback artwork for Now Playing
- fall back through available artwork sources when metadata is incomplete
## Persistence
### `PersistenceStore`
File:
- [Sources/BearWaveCore/Stores/PersistenceStore.swift](/Users/sebastian/Development/radioappmac/Sources/BearWaveCore/Stores/PersistenceStore.swift)
Responsibilities:
- JSON persistence for:
- favorites
- manual stations
- groups
- playback state
- manage application support and cache directories
- import legacy Linux config files on first run where applicable
## UI Structure
Main views:
- [Sources/BearWaveCore/Views/ContentView.swift](/Users/sebastian/Development/radioappmac/Sources/BearWaveCore/Views/ContentView.swift)
- [Sources/BearWaveCore/Views/SidebarView.swift](/Users/sebastian/Development/radioappmac/Sources/BearWaveCore/Views/SidebarView.swift)
- [Sources/BearWaveCore/Views/StationListView.swift](/Users/sebastian/Development/radioappmac/Sources/BearWaveCore/Views/StationListView.swift)
- [Sources/BearWaveCore/Views/StationDetailView.swift](/Users/sebastian/Development/radioappmac/Sources/BearWaveCore/Views/StationDetailView.swift)
- [Sources/BearWaveCore/Views/PlayerBarView.swift](/Users/sebastian/Development/radioappmac/Sources/BearWaveCore/Views/PlayerBarView.swift)
- [Sources/BearWaveCore/Views/MiniPlayerView.swift](/Users/sebastian/Development/radioappmac/Sources/BearWaveCore/Views/MiniPlayerView.swift)
- [Sources/BearWaveCore/Views/MenuBarContentView.swift](/Users/sebastian/Development/radioappmac/Sources/BearWaveCore/Views/MenuBarContentView.swift)
- [Sources/BearWaveCore/Views/SettingsView.swift](/Users/sebastian/Development/radioappmac/Sources/BearWaveCore/Views/SettingsView.swift)
High-level behavior:
- `ContentView` switches between full app UI and mini-player mode
- sidebar chooses radio pages and folders
- station list and station detail cover browse, search, favorites, recents, manual stations, and folders
- `PlayerBarView` stays focused on radio playback controls
## Data Flow
1. User selects a page or search query
2. `StationLibrary` loads data through `RadioBrowserClient`
3. UI renders visible stations
4. User starts playback
5. `AppModel` records playback and forwards to `PlaybackController`
6. `PlaybackController` updates playback state, metadata, artwork, and Now Playing
## Build and Packaging Notes
- The current app bundle is staged by `script/build_and_run.sh`
- SwiftPM resource bundles are copied manually into `dist/BearWave.app`
- Localization directories are copied manually as part of staging
- This is good enough for local development but not yet a polished release pipeline
## Architectural Guardrails
- Keep the app native-first for macOS
- Keep search global and independently modeled
- Keep explicit playback states
- Keep `BearWaveApp` thin
- Do not move product behavior into ad hoc view code when it belongs in a store or service