Skip to content

30 — Global Playback & Mini Player (envisioning)

Envisioning — phased; Phase 1 shipped 2026-06-11 · Phase 2 BUILT 2026-07-06 (xcodebuild green, ⏳ on-device QA — see CHANGELOG; ships with doc-57 §3.2's track-level browse ≠ play core). Governed by 00-ROADMAP.md. Owner's vision: Sonnance should behave like a music player, not just a review surface — audio keeps playing when the screen locks, keeps playing while navigating the app, is controllable from anywhere via an elegant control that costs little-to-no space in the common navigation, and later grows player behaviors (favorites queue, loop, stop-at-end).

Extended by 57-PLAYER-AS-PRODUCT.md (2026-07-05): the player-as-product layer — handoff behavior (browse ≠ play ✅), the favorites data model + queue semantics, and release-preview parity (lock-screen artwork · motion artwork · synced lyrics). This doc keeps the playback mechanics (PlaybackManager, mini player, queue plumbing).

Phase 1 — Background audio + lock-screen transport ✅ (shipped 2026-06-11)

  • UIBackgroundModes: audio — playback continues when the screen locks / app backgrounds (the AVAudioSession.playback category was already set).
  • Now Playing integration (MPNowPlayingInfoCenter): lock screen + Control Center show the version name, track, duration and live position (updated on state changes; the system extrapolates from rate).
  • Remote transport (MPRemoteCommandCenter): play / pause / toggle / scrub from the lock screen, Control Center, and headphones. Targets torn down with the player (no global player yet — leaving the wave page ends the session and clears the lock screen, by design until Phase 2).

Phase 2 — PlaybackManager + the mini player (the architectural step) ✅ (built 2026-07-06)

As built: Core/PlaybackManager.swift (app-scoped @Observable, owns the AVPlayer + Now Playing/artwork/remote + doc-38 analytics + doc-56 TTFA; ObserverHolder retired) · DesignSystem/Components/MiniPlayerBar.swift overlaid at RootTabsView level (cover-thumb play/pause · title · identity-accent hairline; tap → warm reopen via the session's seeded route; swipe down → dismiss + stop). (The native iOS 26 tabViewBottomAccessory renders an empty capsule slot even on empty content — a launch-time ghost bar in QA — so an overlay that draws nothing when hidden is the correct primitive; visibility gated by a wavePagesOnScreen counter so it never double-stacks with a page's own transport. Placement clearance is ⏳ device-tuning, part of the mini-player visual redesign.) The wave page is now a consumer with deck-aware transport passthroughs — and ships with doc-57 §3.2's track-level rules: opening another track while audio sounds never interrupts; explicit play is the switch.

The original envisioning (the AVPlayer lived inside PlayerViewModel and died with the wave page; Phase 2 extracts it):

  • PlaybackManager — one app-scoped @Observable owning the AVPlayer, the loaded track/version, Now Playing/remote commands, and (later) the queue. PlayerViewModel becomes a consumer (the wave page is a view onto playback, not its owner). Leaving the page no longer stops audio.
  • Mini player — the "barely-any-space" control, shown only while audio is active outside the wave page (zero chrome otherwise): a floating Liquid-Glass capsule docked just above the tab bar (~56pt), containing the cover thumb as play/pause (the shipped PlayerBar pattern), a marquee title, and the track's identity-accent progress hairline. Tap → returns to the wave page; swipe down → dismiss + stop. One component, overlaid at RootTabsView level — no layout cost to any screen.
  • Honors composer-focus (hides under the keyboard), Reduce Transparency, and light/dark.

Phase 3 — Player behaviors (after Phase 2)

  • Stop at end (today's default) vs continuous play through the project's tracks → a queue concept.
  • Loop — one track / the queue. → The queue model — a first-class object with sources (album/project ✅ · favorites · single) + modes (repeat off/one/all, shuffle) — is specified in doc-57 §4; album play rides existing Track.order, so it's the cheapest source and leads.
  • Favorites queue — "play my favorites": needs a favorites store (backend: per-user favorites on Track/File — naturally a lens in the doc-26 model), then "favorites" is just a queue source. Data model + queue semantics now specified in doc-57 §4: dedicated Favorite collection ✅, track-level hearts v1, recency ordering; named playlists deferred.
  • AirPlay route button on the mini player / PlayerBar; Dynamic Island / Live Activity as the stretch polish; CarPlay much later.

Sequencing note

Phase 2 is a clean, contained refactor but touches the player's ownership model — schedule it as its own slice (Claude Code, per the layout/architecture routing), after the current player QA round settles. Phase 3 rides on Phase 2 + the doc-26 favorites/lens decision.

Cross-references

Ctrl-Audio Platform Documentation