Volume controls moved to dedicated row with large tap targets
(36pt height, full-width, dark background, percentage readout).
Digital Crown binding removed — buttons are the primary control.
Applied to both local playback and iPhone remote views.
🔴 SmartDJCache concurrent Dictionary crash (April 27+28 crash logs):
- EXC_BAD_ACCESS + doesNotRecognizeSelector in bulkImport()
- memoryCache dictionary mutated from main thread (loadBulkCache)
and background Task.detached (bulkImport) simultaneously
- Fix: NSLock serializes all memoryCache reads and writes
🔴 stopAVPlayer removeTimeObserver crash (April 30 crash log):
- SIGABRT in -[AVPlayer removeTimeObserver:] from Previous button
- timeObserver registered on old player, self.player swapped to
crossfade's active player by finalizeCrossfade
- Fix: remove observer from OLD player at both swap sites before
assignment + reorder stopAVPlayer (observer before replaceCurrentItem)
🟡 Audit fixes (no crash logs, preventative):
- KVO in radioSeekBack wrapped in DispatchQueue.main.async
- Stale vis Task guarded by songId in all MainActor.run blocks
- CHANGELOG.md with full findings documentation