Roadmap: SwiftSonic 0.6 — Ergonomic improvements from real-world dogfooding #2
Pinned
MathieuDubart
started this conversation in
General
Replies: 1 comment 3 replies
|
Well, fortune favours the bold... I'll have a go at implementing this into mine over the next few days and report back any issues I find. Perhaps with a PR if it's anything easy to address. https://github.com/bocan/bocan-music/blob/main/docs/design-spec/phase-19-subsonic.md |
3 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Hey everyone,
After publishing SwiftSonic 0.5.0 with security hardening and full Subsonic +
OpenSubsonic spec coverage, I've been heavily dogfooding the library while
building Cassette — a native
iOS/macOS Subsonic client.
Building a real app on top of SwiftSonic surfaced a few friction points that
were too persistent to ignore. They're not bugs and not blocking, but they
forced workarounds in Cassette that don't belong in consumer apps. 0.6 will
focus entirely on ergonomic improvements to make the library more pleasant
to use without changing its scope or introducing breaking changes (mostly).
Here's what's coming.
🎯 What's coming in 0.6
1.
Hashableconformance on identifiable model typesSwiftUI's value-based navigation pattern (
NavigationLink(value:)+navigationDestination(for:)) requires the navigation target type to conformto
Hashable. Currently, model types likeArtistID3,AlbumID3,Song,Playlist, andPlaylistWithSongsconform toCodable, Sendable, Identifiablebut not
Hashable.This means consuming apps need to add retroactive conformance via extensions,
which is boilerplate that should live in SwiftSonic itself.
0.6 will add
Hashableconformance (keyed onid) to allIdentifiablemodel types. This is additive and non-breaking.
2. Public memberwise initializer on
PlaylistPlaylistcurrently has a synthesized internal memberwise initializer, whichmeans it can't be constructed from outside the SwiftSonic module. This blocks
legitimate use cases like building fallback or empty playlist values in UI
layers, or constructing playlists in tests without going through the API.
0.6 will expose a public memberwise
initonPlaylist, with sensibledefaults for optional fields and a static
Playlist.empty(id:)factory forthe most common fallback case.
3. Direct image data fetching via
coverArtData(id:size:)SwiftSonicClient.coverArtURL(id:size:)returns aURL?(a signed/parameterizedAPI endpoint URL), not the image data itself. This is fine for streaming use
cases where you pipe the URL directly into
AsyncImageor a similar SwiftUIhelper.
But if you want to download and persist the cover art blob (for example
to support offline playback with cover art display), you need to:
coverArtURLto get the URLURLRequest, including any auth or custom headers(Cloudflare-protected hosts require headers that SwiftSonic injects
internally but that aren't exposed when you receive just a
URL)URLSession.shared.data(for:)That's four manual steps that re-implement logic already present in SwiftSonic's
transport layer — including the trickiest part, custom header injection.
0.6 will add a
coverArtData(id:size:) async throws -> Datamethod thatperforms the full fetch internally with all auth and transport configuration
already applied. The existing
coverArtURLmethod stays for streaming usecases.
🛠️ Patch versions
I'm keeping space for
0.6.1,0.6.2, etc. for any small ergonomic fixes oroversights discovered after the 0.6 release. If you hit friction while using
SwiftSonic in a real app, please file an issue — the goal of this minor cycle
is exactly that.
🔮 Beyond 0.6
A quick teaser of what's planned next, in rough order:
moods,
tokenInfo, ReplayGain). Expanding spec compliance into the morerecent OpenSubsonic territory.
interest is there. SwiftSonic stays focused on the Subsonic/OpenSubsonic
spec — anything outside that is community-driven.
💬 Feedback welcome
If you're building something with SwiftSonic and hitting friction I haven't
listed here, please open an issue or comment on this thread. The whole
0.6 milestone exists because dogfooding surfaced real ergonomic gaps — your
feedback can do the same.
Cheers 🎸
All reactions