Blowhole advanced: writing a command tool in Swift 3, and more

Having written my little blowhole command tool to investigate scheduling of Time Machine backups, I decided that it might be more generally useful. For example, there is no command in AppleScript to write anything into Sierra’s new logs, and as a command blowhole can be called by anything which can run a command line – even Eastgate’s Tinderbox and Storyspace!

I therefore wanted a tool which could be invoked with up to two arguments/options: none at all just to write a default message (e.g. run periodically by launchd), one to determine the log level, and another to pass a message which would be written in the log entry. This required parsing the arguments, so my Swift 3 code provides a simple example of how to do that. If you just want to TL;DR and grab the tool, the latest release is available from Downloads above.

Writing entries into the new log system is not as free and easy at it might seem. Much of the content of those entries is determined by static strings, so finding a way of passing information from the arguments to the log message has been the toughest problem to solve. If you pass a normal string variable to be written out, the log system automatically censors it to render that content as <private>, which defeats the purpose.

According to the presentation given at WWDC 2016 on the new log system, you should be able to override this frustrating behaviour by formatting the string with a {public} override, as you’ll see that illustrated in my source code below. However, I have been messing around for a long time trying to get this to work, and can only conclude that, at present, there is a bug in the Swift 3 support for this, or it has yet to be properly implemented. So all you can do for now with blowhole is to pass an integer, which is better than nothing.

A further frustration with Swift 3 is trying to catch errors which occur when converting a string to an integer, using
theInteger = Int(string)
Again, having tried various approaches to catching errors in that, I have for the moment given up and let the tool flunk out with an error code 4. It would appear that most of the methods of catching an error rely on Int() throwing one, which it doesn’t – it just errors out of the whole tool if you try to convert a string containing inappropriate characters.

As with my earlier version, the code to write out to the new log is encapsulated within a public class, which has three different implementations of the method writeLogEntry() to support the three different ways of calling this tool. The third shown below, with a string being passed, shows the code which should result in an arbitrary string being written, but which consistently results in that being rendered as <private>.

blowhole11

Below the Blowhole class is an enumeration to help parse options supplied in the first argument, and a short function to split the letter out from the option supplied when calling the command. These were stolen from Jean-Pierre Distler’s Panagram tutorial example, and seem to work nicely.

blowhole12

The main code initialises an instance of the class, and gets on with parsing any arguments supplied. CommandLine.arguments[0] is invariably the command itself, which we can safely ignore. The other items in the array contain the supplied parameters in order. Here we test the number of arguments supplied, given in CommandLine.argc, then parse CommandLine.arguments[1] and act on the option given.

This could of course be factored out of the main code, which would be worthwhile in a more substantial tool. Although the parsing could go up into the Blowhole class, that would not be appropriate, and it would be better in a separate parser.

I will incorporate a short summary of these in my ongoing articles containing Swift snippets.