30 — Global Playback & Mini Player (envisioning)
Envisioning — phased; Phase 1 shipped 2026-06-11 · Phase 2 BUILT 2026-07-06 (
xcodebuildgreen, ⏳ on-device QA — see CHANGELOG; ships with doc-57 §3.2's track-level browse ≠ play core). Governed by00-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 (theAVAudioSession.playbackcategory 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;ObserverHolderretired) ·DesignSystem/Components/MiniPlayerBar.swiftoverlaid atRootTabsViewlevel (cover-thumb play/pause · title · identity-accent hairline; tap → warm reopen via the session's seeded route; swipe down → dismiss + stop). (The native iOS 26tabViewBottomAccessoryrenders 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 awavePagesOnScreencounter 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@Observableowning theAVPlayer, the loaded track/version, Now Playing/remote commands, and (later) the queue.PlayerViewModelbecomes 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
RootTabsViewlevel — 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
favoritesstore (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: dedicatedFavoritecollection ✅, 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
- Player-as-product behavior (handoff · favorites · release-preview parity):
57-PLAYER-AS-PRODUCT.md - Player layout & components:
22-IOS-DESIGN-SYSTEM.md,PlayerBar - Lenses / favorites direction:
26-IDENTITY-SHARING-NAVIGATION.md - UX backlog (player items):
24-UX-BACKLOG.md