1
Architecture
dallasgroot edited this page 2026-04-08 16:34:58 -07:00
Architecture & Core Concepts
Directory Structure
NavidromePlayer/
├── Shared/ # iOS and watchOS shared code
│ ├── API/SubsonicClient.swift # REST client
│ ├── Audio/AudioPlayer.swift # AVPlayer, AVAudioEngine, FFT
│ ├── Models/Models.swift # Codable API models
│ └── Storage/ # Caching, offline management, multi-server logic
├── iOS/
│ ├── App/ # Entry points, Scene delegates
│ └── Views/ # UI separated by feature (Companion, Library, NowPlaying, etc.)
├── watchOS/
│ ├── App/ # Watch entry and session management
│ ├── Audio/WatchAudioPlayer.swift # Bluetooth/Speaker audio engine
│ └── Views/ # Watch UI
├── project.yml # XcodeGen config
└── generate.sh # Project generator
Core Architectural Systems
Offline Availability (LibraryCache)
All song-list views observe LibraryCache.shared.isServerAvailable to determine UI state. This property is a hybrid signal combining:
- NWPathMonitor: Detects local network drops.
- ServerManager.connectionState: Detects remote server unreachability.
A hasEverConnected guard ensures the UI doesn't flash offline states during the initial app launch ping.
Status Bar Dynamic Coloring
To keep the app entirely in Dark Mode while allowing the status bar icons to flip to black for bright album covers:
- The root window is a
NavidromeHostingController(aUIHostingControllersubclass). - It observes
AlbumColorExtractor.shared.preferredStatusBarStyle(threshold > 0.88 brightness). setNeedsStatusBarAppearanceUpdate()is called to toggle the icons, leaving the SwiftUI.preferredColorScheme(.dark)intact.
Smart DJ & Queue Reactivity
The SmartCrossfadeManager relies on a dual-AVPlayer (A/B) engine:
- Queue mutations (
playNext,playLater, etc.) triggernotifyQueueChanged(). - Standby players are pre-loaded via
prepareNextForCrossfade(). - If a queue mutation happens during a crossfade,
queueChangedDuringFade = trueflags the engine to recalibrate vianeedsNextTrack()immediately after the fade completes. - Scrobbling fires safely inside
needsNextTrackon the crossfade path to avoid double-counts.
Caching Strategy
The app utilizes a robust, cache-first loading system for zero-spinner warm launches:
- LibraryCache: JSON cache for library structure.
- ImageCache: Two-tier (NSCache + disk JPEG) with a 200MB LRU limit.
- SmartDJCache / VisualizerStorageManager: Local storage for audio analysis profiles and FFT frames.
- WaveStateCache: Preserves the active waveform array during flight animations between the Mini Player and Dynamic Island.