Appcasting and Software Update

Fraser Speirs came up with the idea of Appcasting. Shortly thereafter, Andy Matuschak created Sparkle as the appcasting equivalent to UKUpdateChecker. While MacPAD is easily the more powerful format, Appcasting is based on RSS, which is a more widely used format. Also, an appcast feed can be viewed in the world's best web browsers (e.g. NetNewsWire) as well as be parsed by update-checkers. So, I'm planning to add appcasting support to Shovel.

There's only one small problem with that: While an application that uses appcasting to check for updates to itself already knows where it is on the hard disk, Shovel may only have the feed URL and now needs to look up the correct application for a feed. The best way to do that is usually to use the bundle identifier on Mac OS X. But how does one add that information to an RSS feed? Yes, I could just define my own "appcast" namespace and add some additional tags to a feed. But many tools for creating RSS feeds would need to be explicitly updated to support this.

Then I realised something: All MacPAD applications contain a MacPAD.url file that contains the URL to the file with the update information. Shovel has the same URL in its database. So, why not just add a standard URL file to the application that Shovel can check and compare to its feed URL? Then I checked out Sparkle, and 'lo and behold: It has an SUFeedURL key that you specify in your app's Info.plist which contains the appcast feed URL.

The SUFeedURL key

Here's my proposal: Every application that uses appcasting should put an SUFeedURL key in its Info.plist, even if it doesn't use Sparkle. Shovel and other software update clones can then look up this key and compare it against a feed URL from their databases to determine whether they're dealing with the same application. Moreover, since the Info.plist is localizable, you can simply add a different URL to your InfoPlist.strings for a particular language if you have to support several locales.

First problem solved: I can find a particular application. Trouble is, how do I make sure the newest update notification is for my application and is for a newer version than the current one? The latter point is already solved by Sparkle: The download's name is always required to have the version number at its end (e.g. MyApp_1.2.dmg).

But how do I find whether the attached file is just an enclosure (like an MP3 file in a podcast), or an update for another application from the same distributor, or an update for my app? The solution lies, again, in the naming of the download: The name of the download must start with the executable name of the application. The executable name (i.e. the name of the file inside the MacOS folder) can't be changed by the user. It also doesn't change when the user renames the application package. And in concert with the feed URL can be used to uniquely identify an application.

Why all of this when there's already MacPAD?

MacPAD is a plist-based file format. As such it is very easily parsed by a Mac application, but kind of awkward to handle for web servers, software update sites and others interested in being current about new software releases. Also, there are currently few tools to edit MacPAD files. OTOH, RSS is a huge market already, and lots of software and web-based CMS-like systems already exist for it. This Appcast proposal gives you most of the features of MacPAD, without tying you to Mac-specific niche technology.

I'm a pointy-haired boss. Can you summarise it?

Here's the steps you have to take to make your app appcasting-savvy:

Create an RSS feed that contains the download for the newest version as an enclosure.

Make sure the names of the downloads follow the pattern:ExecutableName_versionNumber.tgz
(it doesn't have to be a TGZ archive, but right now that's the only scheme Sparkle understands. Shovel and other solutions in the future OTOH will be perfectly happy with .dmg's, .zip, and even .sit files if StuffIt is installed on the computer)

Add an SUFeedURL key to your Info.plist (and optionally to InfoPlist.strings) containing the URL for this feed.