More fun scripting with Swift and Xcode: An app in a couple of hours

I had been thinking about how best to bare the innards of Grand Central Dispatch (GCD) for a few days. Apple doesn’t provide any calls which give the slightest idea as to what is going on with them, nor are there any command tools of use. The only way that I have been able to see inside them is by browsing my log using Consolation, and that has not been particularly simple.

Yesterday afternoon, while out walking on the local Downs in glorious sunshine, I realised that the only way ahead was to analyse GCD’s log entries. When we returned, there was an ominous comment on this blog from someone with Time Machine problems on some new servers, which could only be investigated further by examining GCD in the log.

Over the next couple of hours, admittedly stealing generously from the source code of Consolation, I put together and tested the first beta-release of DispatchView. If there is one real-world test that I have done to determine Swift’s fitness for scripting tasks, this is it.

My concept was to have a minimum of controls: just two to set the period to examine, plus a button to make it all happen. Ideally I’d like the app to be able to analyse real-time streams of log entries, but in this initial version I’ll settle for looking at the recent past. Future releases should analyse the log entries to reveal when DAS did its last rescore, how many activities it had in its queue then, and so on. But for now, just displaying the selected log entries would be a huge step forward.

I created a new document-based app for macOS in Swift, and laid its window out in Interface Builder. The numeric text box and popup were copied and pasted from Consolation’s window, as was one of the large scrolling text panes below (the other being duplicated from that). With log entries to display, most users would want to resize the view to span much of the display, so I was very careful to ensure that behaviour on resizing was correct before I wrote a single line of code.

The plan for the code was simple: click the button, and two almost completely hard-coded log show commands would be finished, and run. The standard output from each would then be sent to the two scrolling text panes.


The preliminaries in the View Controller are also simple. You can see the hard-coded string arrays, ready to have the final user-selected time period added, e.g. as "5m". There’s no need to save any preferences, with so few controls, so setting up the view is just a matter of building the popup menu and inserting a sensible default time period.


This is where the work gets done.

The first few lines work out that terminal string for the command arguments. These must be carefully constructed as an array of strings: one glitch and the command spits back an error:
let timeSelStr = timePopup.titleOfSelectedItem!
theTimeLet = String(timeSelStr[timeSelStr.startIndex])
let theTimePCons = String(theTimePeriod) + theTimeLet
var theFullCmdStrCTS = theBaseCmdStrCTS
var theFullCmdStrXPC = theBaseCmdStrXPC

Next the Process to be run is set up, its standard output piped back, then launched, and that output captured:
let taskCTS = Process()
taskCTS.launchPath = "/usr/bin/log"
taskCTS.arguments = theFullCmdStrCTS
let outPipeCTS = Pipe()
taskCTS.standardOutput = outPipeCTS
let fileHandleCTS = outPipeCTS.fileHandleForReading
let dataCTS = fileHandleCTS.readDataToEndOfFile()

If that has completed without error, the returned output is sent to the pane, and that is repeated for the XPC entries:
let statusCTS = taskCTS.terminationStatus
if statusCTS != 0 {
doErrorAlertModal(message: ("log command returned error number \(statusCTS)"))
} else {
let stringCTS = NSString(data: dataCTS, encoding: String.Encoding.utf8.rawValue)!
let attrCTS = NSAttributedString(string: stringCTS as String)
let taskXPC = Process()

and so on for XPC.

Once I was happy that was all good and worked properly, I finally added some code, again stolen from Consolation, to save the concatenated log extracts as UTF-8 text.

The problems now rest in where to go from here. I want to parse the log entries, and that is painfully difficult with them in an unstructured format as they are here. It will be much better to work using the extracts in JSON format, which I can readily convert into a Swift Dictionary for further analysis. But I will then have to use the Dictionary to construct cleaner text output for display in the scrolling text panes.

I’ll come back to those another day.