Writing SparkleCheck in AppleScript: don’t read the manual

When there are several different ways to solve a problem, I usually look to the documentation to guide me towards the best approach. When I was working out a quick AppleScript method of checking for old and vulnerable versions of Sparkle Update, Apple’s extensive if poorly-organised manual led me to a waste of time – although there are some interesting lessons learned.

The objective

If you open up an app which uses Sparkle Update, it is not hard to see what is required. Support for the Sparkle system is provided in a framework named Sparkle.framework, which is located in the path appname.app/Contents/Frameworks/. In the Finder, there are several different ways of discovering the framework’s version number: you can ask for the short version of the .framework itself, or that of the app inside it which does the updating, Sparkle.framework/Resources/Autoupdate.app, or you can look it up in the property list for the framework, at Sparkle.framework/Resources/Info.plist, or any other aliased approaches to reach those files.

The manual

I didn’t fancy trying to extract the short version from the XML of the property list, at least not without an easy and universal way of parsing the XML, which isn’t well supported by bare AppleScript. But the documentation offered me a way to get info for an app, file, or folder, so that was looking promising.

Another issue which I needed to resolve was building a list of apps. Although I knew that I could trudge through a list of all items in the folder Applications, I didn’t want to test whether each was an app or not. The documentation again was eager to help me out, offering choose application to put it all in user control.

So my first draft solution ran something like:

  1. choose file name and open for access to create the text file to contain the output
  2. choose application with multiple selections allowed to select the apps to check
  3. build the path to Sparkle’s Sparkle.framework or Autoupdate.app
  4. get info for that framework or app
  5. get short version of to extract the short version from that info
  6. write to the output file the app name or path and the Sparkle version.

I would then wrap much of that in a try … on error cover, so that if the app didn’t support Sparkle, and Sparkle.framework didn’t exist, that would tell me not to bother, and to ignore it, or write out that the app did not use Sparkle.

If you have used get info for an app, you will by now have realised the problem that I was about to encounter. AppleScript does not dive into the app bundle itself and find the info out, it opens the app and asks it, using AppleEvents. So, with over 500 apps in my Applications folder, if I asked all of them, I’d end up with over 500 apps running.

One kludge around that is to close each app once you are done with it, using tell theApp to quit but this is still definitely not a good way to proceed. There is also the ever-painful process of making sure that paths are passed in the right format – POSIX or Mac – which usually wastes some of my time before I have got it right. Thankfully working in ScriptDebugger makes it so much easier and quicker to sort those things out.

It was at that point that I resolved not to follow the documentation, and only to read the manual when I really had to.

The cheat

One of the many powerful features of AppleScript is that it is a friendly and easy way to wrap shell commands, and there is of course a straightforward shell command which will extract the short version from any property list file, along the lines of
defaults read [path to .plist] CFBundleShortVersionString
and it was that which looked the next best approach. You can read my commented source code here : sparklecheckcmt

sparkcheckscpt1

I start by defining the POSIX path to the property list which I want to access, then get on with creating the output file, and opening it ready to receive text.

sparkcheckscpt2

There is a neat but now deprecated way to list all the items in a folder, but the preferred technique uses the hidden System Events app working on aliases (more time getting POSIX paths and aliases messed up).

sparkcheckscpt3

We then iterate through all the items in that list using repeat. For each, we first test whether its name ends in .app, and must presumably be an app. If it is, we then build the correct shell command defaults read … to obtain the short version number.

sparkcheckscpt4

Calling the shell command is inside a try … on error wrapper. If the command returns an error, we’ll presume that’s because the path was invalid because there is no Sparkle.framework. If it doesn’t, then we’ll use the version obtained by the shell command, and write it out to the output file.

sparkcheckscpt5

Finally, and most importantly, we close the code structures, and close the output file.

Put it all together and it should look something like this:

sparkcheckscpt6

So rather than reading the manual, there’s something to be said for expressing the kernel of the task as a shell command, then wrapping the AppleScript around it.