Spotuify macOS App
Spotuify macOS app
Native SwiftUI client for Spotuify. It exists for the times a terminal TUI
is the wrong surface: menubar controls, a normal macOS window, system media
integration, and a friendlier first impression for people who still want the
daemon-backed product.
What it is
The app lives in clients/macos/. It is not a second backend. SpotuifyKit
speaks the same daemon IPC contract as the CLI and TUI, renders daemon state,
and sends daemon requests. Playback still belongs to the Rust daemon and
embedded librespot.
Line to keep: the macOS app is another client of the core, not a fork of the
product.
Current shipped surface
Validated against clients/macos/ on 2026-08-12:
- The app has a normal window, menubar controls, and a mini player.
- Its views cover now playing, search, library, playlists, queue, history,
devices, lyrics, podcasts, reminders, and settings. - The player view includes cover art, synced lyrics, and the same live spectrum
data published by the daemon. SpotuifyKitowns IPC framing, provider-aware wire models, daemon lifecycle,
stores, updates, and protocol compatibility checks.- The app schedules local reminder notifications from daemon-owned reminder
state. It does not invent a second reminder system. - Protocol parity fixtures and live-daemon tests check the Swift client against
the Rust request contract.
The app has grown beyond a menubar remote, but its reason for existing has not
changed. It is the native macOS view of the same local music service.
Why it exists
The terminal is the canonical surface, but a shipped music product eventually
needs a calmer daily-use client:
- a menubar presence,
- a real window for browsing,
- update prompts that make sense on macOS,
- daemon startup and install handoff without asking the user to think about the
backend every time.
The app should make Spotuify easier to live with without weakening the
CLI-first contract. If a capability only works in the app, that is still a
spotuify product bug.
Packaging truth
The DMG is built by clients/macos/scripts/build-dmg.sh.
Current code truth:
- the script builds
Spotuify.app, - bundles a universal
spotuifybinary intoContents/Resources/spotuify, - signs with a Developer ID when a signing identity is available,
- notarizes only when
SPOTUIFY_NOTARY_PROFILEis set, - falls back to an unsigned/ad-hoc-signed DMG when those credentials are not
present.
The tag-driven CI release does not build this DMG today. It ships CLI archives
and Homebrew. That means the DMG is a manual release artifact unless the release
workflow changes.
Current risk
The easy docs mistake is to talk about "the macOS release" as one thing. It is
at least two surfaces:
- CLI tarballs from CI, currently unsigned/not notarized but shipped with
checksums and attestations. - The SwiftUI DMG, locally built today, with signing/notarization depending on
local credentials.
Those are both valid if the docs name them honestly. They become broken
promises when the install page says "latest signed build" while the workflow
cannot produce that build.
The distinction was rechecked on v0.1.102. The CLI workflow contains guarded
Developer ID and notarization steps, but both macOS jobs logged that signing
secrets were missing and shipped unsigned archives. A green optional step proves
that the workflow completed, not that the artifact was signed.
Contract lesson
The macOS client makes Client contract drift is product drift concrete.
Matching type names in Swift and Rust are not enough. The protocol version
gate, request-kind fixture, provider-policy fixture, wire-decoding tests, and
live-daemon suite are what keep "another client" true as the daemon changes.