PreviewMarkdown, my modern QuickLook plug-in for Markdown files, was recently updated to version 1.1.4. It was a minor change, which is why it wasn’t announced here, but the reason for the update may interest to anyone keen to understand the workings of macOS. It also highlights some inconsiderate behaviour on the part of some Mac software developers.
The PreviewMarkdown update adds support for a specific Uniform Type Identifier (UTI):
net.ai.markdown. UTIs are the means by which macOS associates file types with the applications that can edit or view files of those types.
Apps declare UTIs for their own data files in their
Info.plist files. Pick any app in your Mac’s Applications folder, right click on its icon and select Show Package Contents. Select the
Info.plist file, press Space to preview it and look for
<key>UTExportedTypeDeclarations</key> in the previewed XML. Under this key are the file types the app declares. Each has a UTI which is listed under
This is great for apps which have their own file types. For example, my app Squinter defines the UTI
com.bps.Squinter for the Squirrel-language project files it works with. These files only have meaning for Squinter, so the UTI is appropriately unique. If someone double-clicks on a
.squirrelproj file, Finder uses the file’s UTI association to open Squinter and no other app. If you haven’t installed Squinter, you are very, very unlikely to have any
.squirrelproj files kicking around your SSD.
Finder uses a framework called LaunchServices to maintain a registry of all the UTIs your system has encountered. This registry is updated whenever you install a new app, and it’s checked when Finder starts up, which most commonly occurs when you log into your user account on your machine. LaunchServices is managed from the command line using a tool tucked away in the System folder:
The problem lies with public file types. These files may be able to be opened by multiple apps on your system. We’ve all been there: we get a new Mac, install apps, copy over files and double-click on, say, a text file only to have it open in macOS’ TextEdit rather than our preferred text tool. Or we install a new app, B, and suddenly all the files that used to open in app A are opening in B. If that’s what we want, fine. But it’s a pain if we still want to use A for those files.
Of course, we also know the solution: get the file’s Info and change its Open with: setting to the app we want to use. Click on Change All… to make sure all such files are opened this way. The pop-up below Open with: lists all the apps Finder, via LaunchServices, knows are able to open files of the type you selected.
But what happens if the app interested in the file type isn’t an editor app? That’s the case with PreviewMarkdown. It is associated with Markdown files but unlike Xcode, Visual Studio Code, BBEdit, IA Writer, vim, FoldingText and many others, it doesn’t want to edit them. That’s why if you try and select it as an app from a Markdown file’s Get Info… panel, you’ll be told ‘It’s not known if this app can open “sample.md”.’
How does PreviewMarkdown get access to Markdown files, then? When you QuickLook a file, macOS checks the file’s UTI against its registry. PreviewMarkdown’s component app extensions list a series of Markdown UTIs; these lists are read when you install the app. If you QuickLook a file with one of those UTIs, macOS executes the Previewer app extension to generate a preview which macOS then presents on screen.
What happens when two or more apps on the system can preview the same UTI is, as they say, undefined behaviour!
For Markdown files, one of the most common UTIs is
net.daringfireball.markdown. Daring Fireball is the website authored by Jonn Gruber, who devised Markdown way back in the noughties. This UTI is the closest we have to an official one. But it’s not the only one, and a variety of app developers have effectively grabbed Markdown file ownership for themselves by defining their own UTI rather than simply making use of the semi-official one above. IA Writer developer iA, Inc. is a case in point: IA Writer declares the UTI
net.ai.markdown for Markdown files. In the past it also used
This is why PreviewMarkdown currently lists half a dozen UTIs which have appeared over the years as apps have sought to take ownership of a system’s Markdown files, and why I recently had to add
net.ai.markdown to that list. To be fair to IA Writer and all the others, there’s nothing malicious about what they’re doing. Using a non-standard UTI for Markdown files was done to ensure that when a user of one of those apps double-clicks on a Markdown file, the file is correctly passed to the app. This is, after all, good UX.
But here’s the thing: Markdown is a formatting language used primarily by techies, and these folk know how to get app A rather than app B to open file type X. They don’t need the kind of hand-holding IA and others provide.
Markdown is now so widely used, and there are so many tools that now support it, that there really is no longer any need whatsoever to tightly manage its usage the way that IA Writer and other apps do. I’m not saying IA Writer should not include a Markdown files UTI among its list of the file types it can open — since it can open these files, it should include a UTI — but that it should use the de facto standard
net.daringfireball.markdown to do so, not a proprietary one.
Everyone wins if as many apps as possible use the standard UTI. A user with only IA Writer (or whatever) can continue to open Markdown files with that app, so no change there. Users with a variety of editors capable of handling Markdown files can use Finder to pick the one they want to open those files. Again, no change for most folks. And I won’t have to update PreviewMarkdown every time a new Markdown UTI emerges. That’s a win for me, and developers like me, at a cost to no one.
So, software developers, if your app declares its interest in Markdown files, good. Please continue to do so — it’s a great facility. But please do so in an open way that doesn’t associate this open standard with a proprietary app in order to squeeze out everyone else.
This is, after all, why we have public UTIs — to stop the open becoming closed.