A Bug Hunting Story: More Fun in the Land of Drag & Drop
• Chris Liscio
• Chris Liscio
It’s not uncommon for me to start my day with a random song stuck in my head. I don’t necessarily hear the song on TV or the radio—it just starts playing in my mind, uninvited, and wants to come out.
Today, it was Dua Lipa’s Don’t Start Now. The chords, that bass line, the groove—I mean, come on! If you know the song already, and are anything like me, it’s playing in your head right now. Sorry! (Not sorry…)
This happens to me quite a bit, and sometimes the only way to scratch this itch is to get myself near an instrument (this time, the piano) and load the song into Capo to work it out.
I usually use my iPhone for this little ritual of mine, because it’s the easiest way that I know to “upgrade” the song from a streamable Apple Music track to a DRM-free copy that I can mess around with in Capo. Also, it’s always in my pocket and ready to go!
After a few minutes of me hacking away at the piano, Shelley noticed that I had bought the song to learn it. She likes to change up her collection of tracks that she uses for our QA testing, and added it to her library by plucking it from the list of Family Purchases.
Out of habit, she launched Capo and dragged the song over from the Music app. Nothing happened, and we were excited!
The worst kind of bugs are the ones that you can’t reproduce. Ever since macOS 10.15 Catalina shipped, we started to hear reports of issues dragging & dropping songs from Music.app into Capo.
We have a handful of machines here, each with a bunch of test partitions running different versions of macOS that we support. Despite that, we have been unable to reproduce this issue on any of those systems, or our own day to day machines.
Fortunately, there was always an easy workaround for this: Right-click on the song in Music.app, show it in the Finder, then drag the file over to Capo. The workaround isn’t horrible, but I still don’t like that this feature isn’t working as it should. But I can’t really fix things like this unless I can recreate them in a controlled environment.
So, yeah—that’s why we were happy to encounter this. We finally had a machine where we saw the issue.
Even better is that I was able to head to my machine, repeat Shelley’s actions, and I got my own Music.app into this strange state. Specifically, I downloaded the song that I just purchased, headed to my “Recently Added” songs list, and tried to drag it into Capo. It didn’t work—Awesome!
Now that I could reproduce the issue, I could see exactly how the bug looks to the user:
So what’s the difference between the songs that work, and the songs that don’t?
Capo’s “Drag Song Here” view is registered to accept URLs on the pasteboard, and an extra blob of metadata that iTunes/Music put onto the pasteboard with the type, com.apple.itunes.metadata
. Neither of those types were included on the drag pasteboard for those troublesome songs, and the songs that worked seemed to only have the NSPasteboardTypeURL
data. Strange!
Usually, I turn to Apple’s ClipboardViewer sample code to see what’s going on in these situations. To use it, I start a drag operation from the app that I’m snooping on, and then the Clipboard Viewer will display the contents of the Drag Pasteboard for me.
Unfortunately, according to the Clipboard Viewer, Music didn’t appear to be putting anything onto the pasteboard. I don’t know if it’s a problem with the sample code, or perhaps Music behaves oddly, but I wasn’t getting anywhere with that tool.
So maybe something else has changed…
Using strings
at the command-line, I discovered quickly that Music.app has since changed its drag pasteboard UTI to com.apple.music.metadata
. Argh!
I hadn’t noticed this change because Capo prefers to use the song file’s URL when it is available. Because this URL is usually there, the metadata is not consulted.
The main reason that Capo even looks for this metadata is that the URL is expected to be missing when the song has not been downloaded from the cloud yet. Capo uses the metadata to confirm this, and presents an appropriate message to the user in this case.
So that’s the crux of the issue—the songs that fail to drag into Capo are missing their URL entry on the drag pasteboard. Fortunately, I can also retrieve the song’s file URL from the metadata…
At least, that’s what I thought! I adjusted Capo’s logic to look for the com.apple.music.metadata
, and it still wasn’t working! What gives?!
This is where things get really weird.
Still in the debugger, I decided to drag one of the working songs from Music.app into Capo, and ask the pasteboard what data was included. Here’s the list I got:
<__NSArrayM 0x6000023fdd10>(
public.file-url,
CorePasteboardFlavorType 0x6675726C,
dyn.ah62d4rv4gu8y6y4grf0gn5xbrzw1gydcr7u1e3cytf2gn,
NSFilenamesPboardType,
dyn.ah62d4rv4gu8yc6durvwwaznwmuuha2pxsvw0e55bsmwca7d3sbwu,
Apple URL pasteboard type,
Dsid,
Oidl,
com.apple.tv.metadata,
CorePasteboardFlavorType 0x6974756E
)
Okay, so we’ve got our URL types in the list—that’s expected for a working file. But wait, WTF?! What the hell is up with com.apple.tv.metadata
?!
Sure enough, when I dumped out the data for that type, I found the metadata I was looking for.
My first thought was, “I bet this is a LaunchServices issue!” The system’s database of UTI types can get downright broken sometimes—usually because an app has “poisoned” it by mis-using the Exported & Imported UTI types. But for a brief moment, I wondered if there might have been a typo somewhere in the code for Music.app.
Fortunately, after running strings
again on the Music.app executable, I confirmed that there was no mention of com.apple.tv.metadata
in there. So yeah, it seems like something related to LaunchServices, and it’s probably the TV app’s fault.
Time to check the plists!
Here’s the type declaration from Music.app:
<dict>
<key>UTTypeConformsTo</key>
<string>public.xml</string>
<key>UTTypeDescription</key>
<string>Metadata</string>
<key>UTTypeIdentifier</key>
<string>com.apple.Music.metadata</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>com.apple.ostype</key>
<string>itun</string>
</dict>
</dict>
And what about TV.app?
<dict>
<key>UTTypeConformsTo</key>
<string>public.xml</string>
<key>UTTypeDescription</key>
<string>Metadata</string>
<key>UTTypeIdentifier</key>
<string>com.apple.TV.metadata</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>com.apple.ostype</key>
<string>itun</string>
</dict>
</dict>
I think I see the problem now.
Both apps declare different UTType identifiers, however both of them specify the same OSType (a.k.a. “fourcc” or “four char code”) value of itun
.
Somewhere under the hood, the data that is put on the pasteboard by Music.app is given the incorrect type identifier—the one that’s declared by TV.app!
The simple fix is to also add the TV metadata type to Capo’s list of supported UTIs in the “Drag Song Here” code, but I really don’t want to do that.
Fortunately there’s an API that’ll help me out here. UTTypeCreateAllIdentifiersForTag
(or its modern replacement) can provide me with a list of all the registered identifiers for the itun
tag, and I can search for any of those UTIs in the pasteboard.
On my machine, that API returns UTIs for both the Music.app and TV.app variations of the metadata. Furthermore, the fix should continue working correctly if Apple decides to fix this in the TV app. Specifically, I would prefer that Capo doesn’t respond to users dragging unsupported stuff from the TV app.
Long-time blog readers might recall that I’ve fought with this stuff before, but I have long since removed that old workaround from Capo.
Why? Because iTunes had been correctly included the URLs on the pasteboard for years without any issues that I can recall. Exposing a service from Capo to handle the iTunes metadata was a gross hack, but I think I need to bring it back.
This is where things get unfortunate. I need to specify that I support both the Music and TV app variations in my “workaround service” in order to accept drags to Capo’s Dock icon for these items in Music.app that are missing their file URLs. Argh!
It’s not the end of the world to do this, but it’s not the kind of code that I enjoy shipping. Sure, it gets stuff working for folks that are affected by these bugs, but I need to keep my eye on this code because it’s delicate, and it could lead to new issues down the road.
Ha—that’d be nice, huh? Remember the bits above about QA testing, multiple machines and partitions, different OS releases?
Just because I fixed this all on my machine, it doesn’t mean that I’ve fixed anything at all. Furthermore, it doesn’t mean that I didn’t break something else in the process.
The fix will be made available shortly for these random drag failures from Music.app, but in the meantime: Right-click on the song in Music.app, show it in the Finder, then drag the file over to Capo. :)
If I took anything valuable away from this whole bug-hunting ordeal, it’s that I need to let myself mess around with songs when they get stuck in my head!
Working on Capo keeps me away from the joy of playing music more than I’d like to admit, so it’s nice to know that giving in and enjoying myself sometimes turns out to be a productive exercise.