Create BearWave macOS radio-first app
This commit is contained in:
178
ARCHITECTURE.md
Normal file
178
ARCHITECTURE.md
Normal file
@@ -0,0 +1,178 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user