Apple Music gives the same album two different IDs
This app was later renamed Deep Cut Atlas. It’s called “Discoverer” throughout below, because that’s what it was called on the day this happened.
This week’s feature in my discovery app is the “liked it” path: you’re looking at an album you saved to check out, you tap Add to Library, and the app immediately offers you more releases from that same artist to queue up next. Add, then keep the momentum going. Building it taught me something about MusicKit I didn’t expect, and it’s the kind of thing that would quietly break a naive implementation.
The flow
Tap “Add to Library” on an album card and three things happen: the album’s tracks go into your library, those same tracks come out of the “To Check Out” playlist (you’ve dealt with it, it shouldn’t linger), and a sheet slides up: “More from Khruangbin.” That sheet is a list of other releases by the artist - newest first, filtered to the album/EP/single/comp types you care about, and crucially, not showing anything you already own. Tap one, it goes into the playlist, sheet dismisses. Don’t want any? Swipe it away.
The first two steps reused service methods I already had. The sheet is where it got interesting.
”Don’t show me what I already have”
That filter sounds trivial. I have the artist’s catalog releases, I have the user’s library - subtract one from the other. My instinct was to compare by id, the way you’d dedupe anything.
That doesn’t work. MusicKit gives the catalog copy of an album and the library copy of the same album different identifiers. The album sitting in your library and the album you just pulled from a catalog search are, as far as their MusicItemID goes, two unrelated objects. So a set-membership check on ids finds zero overlap and happily shows you albums you already own.
Once you see it, it makes sense - your library item is a personal copy with its own lifecycle, the catalog item is the public store listing - but it’s not obvious until it bites you. The fix is to match on something stable across both: a normalized title-plus-artist key.
let libraryKeys = Set(libraryAlbums.items.map {
albumKey(title: $0.title, artist: $0.artistName) // lowercased, trimmed
})
return catalogAlbums
.map(LibraryAlbum.init)
.filter { !libraryKeys.contains(albumKey(title: $0.title, artist: $0.artistName)) }
It’s a heuristic, and I’ve written it down as something to validate on a real device. A deluxe edition and a standard edition share a title; a remaster might not. But it’s far closer to right than an id comparison that’s structurally guaranteed to fail.
Finding the artist at all
There’s a related wrinkle. My playlist groups identify their album by a synthesized key, not a real catalog id - because, as I wrote about in the album-grouping post, a playlist track doesn’t carry its album’s full identity. So to find “more by this artist” I can’t look the artist up by id either. I search the catalog by name, take the first hit, and ask for their albums. Same imprecision (two artists can share a name), same note-to-self to verify on device.
I’m noticing a theme across this project: MusicKit is happiest when you hold real catalog identifiers, and a lot of the work is reconstructing them from the looser data you actually have in hand.
Building it blind, still
As with every feature so far, none of this ran against my real library while I built it - MusicKit doesn’t work in the simulator, and I’m still on a free developer account. The mock service returns a canned set of Khruangbin releases, deliberately including the album you “added” and spanning a few types and years, so the exclude-it / filter / sort logic has something real to chew on. Twenty-two unit tests, all green, all against fake data. The honest verification - does my real library actually get filtered out, does the artist search find the right Khruangbin - is queued up for the day I plug in a phone with a paid account behind it.
The takeaway
If you take one thing from this: in MusicKit, “is this album already in the user’s library?” is not an id question. Identity in a music catalog is messier than identity in your own database, because the same record exists in two places that don’t know about each other. Match on what’s stable to humans - title and artist - not on what’s stable to one system.
Related reading
The mirrored window lied about the phone being unlocked
A one-line grouping fix that was already written, and three walls between it and proof: actor isolation, log stream's Mac-only scope, and a mirrored session that looks unlocked when the device isn't.
Pre-release albums, and the fix I couldn't fully verify
Apple's 'Track N' placeholders rendered as real data. The one detection signal I couldn't confirm became the one signal I stopped depending on.
A subscribe button, a MusicKit API I'd never used, and a sheet that dismisses into silence
musicSubscriptionOffer presents Apple's native sheet - and nothing in the app would ever notice it closing. The correctly-coded action that was still a functional dead end.