Skip to content

refactor: migrate to @Observable and fix VolumeController initialization#26

Merged
joshhbk merged 2 commits intomainfrom
fix/volume-controller-initialization
Jan 31, 2026
Merged

refactor: migrate to @Observable and fix VolumeController initialization#26
joshhbk merged 2 commits intomainfrom
fix/volume-controller-initialization

Conversation

@joshhbk
Copy link
Owner

@joshhbk joshhbk commented Jan 31, 2026

Summary

  • Migrate all ViewModels and services from ObservableObject to @Observable macro
  • Create centralized AppSettings for environment-based settings management
  • Replace NotificationCenter-based communication with direct observation
  • Fix VolumeController initialization crash and playback state lag

Changes

@observable Migration

  • ShufflePlayer - Remove NotificationCenter observer, expose reshuffleWithNewAlgorithm(_:)
  • AppViewModel - Use withObservationTracking replacement for scrobble forwarding
  • LibraryBrowserViewModel - Replace Combine debounce with Task-based approach
  • SongUndoManager, ArtworkLoader, AlbumArtColorExtractor - Simple migrations

Environment-Based Settings

  • New AppSettings class centralizes shuffle algorithm and sort option
  • Settings views modify appSettings directly (no NotificationCenter)
  • MainView uses onChange to forward settings changes to player

View Updates

  • Replace @StateObject@State for owned @Observable classes
  • Replace @ObservedObject → plain var for injected classes
  • Use @Bindable for bindings, @Environment for injected state

Bug Fixes

  • VolumeController - Move init to onAppear, remove crash-causing assertionFailure
  • Playback lag - Make observation Tasks @MainActor to avoid actor-hopping delays

Test plan

  • App launches without crashes
  • Build succeeds
  • Play/pause responds immediately (no lag)
  • Volume controls work on first use
  • Settings changes (shuffle algorithm, sort option) take effect
  • Scrobbling still works

🤖 Generated with Claude Code

joshuahughes-qm and others added 2 commits January 31, 2026 15:24
MPVolumeView needs to be in the view hierarchy and laid out before its
internal slider subview is accessible. Previously, volume adjustments
could fail silently if called before the view was ready.

- Add explicit initialize() method called from ShuffledApp.init()
- Force layout after adding to hierarchy so subviews are populated
- Consolidate increase/decrease into shared adjustVolume(by:) helper
- Keep assertionFailure for catching issues during development

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
## @observable Migration
- Migrate 6 classes from ObservableObject to @observable macro:
  - ShufflePlayer, AppViewModel, LibraryBrowserViewModel
  - SongUndoManager, ArtworkLoader, AlbumArtColorExtractor
- Replace @StateObject/@ObservedObject with @State/var in views
- Use @bindable for bindings from @observable objects
- Use @ObservationIgnored for non-observed internal state

## Environment-Based Settings
- Create AppSettings (@observable) to centralize app settings
- Add Environment keys for appSettings and shufflePlayer
- Replace NotificationCenter-based communication with direct observation
- Settings views now modify appSettings directly

## VolumeController Fix
- Move initialization from App.init() to MainView.onAppear
- Remove assertionFailure that caused crash when no window available
- Add lazy initialization fallback in adjustVolume()

## Performance Fix
- Make ShufflePlayer's playback observation Task @mainactor
- Simplify AppViewModel's scrobble observation to avoid actor-hopping
- Fixes multi-second lag on play/pause UI updates

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@joshhbk joshhbk changed the title fix: initialize VolumeController at app launch to prevent race condition refactor: migrate to @Observable and fix VolumeController initialization Jan 31, 2026
@joshhbk joshhbk merged commit 8398f6e into main Jan 31, 2026
1 check failed
@joshhbk joshhbk deleted the fix/volume-controller-initialization branch January 31, 2026 21:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants