Xcode Swift playgrounds 3: shell scripts, droplets, and actions

Last time I looked at scaling, ease of producing little scripts, and had my first success in scripting file actions using Xcode’s Swift playgrounds. This next session tried to ride that same wave of success, looking at making command shell calls, producing droplet apps, and folder actions. These proved more difficult in parts.

A lot of tools created using scripting systems such as AppleScript and Automator work primarily by putting a friendly wrapper around a command which you could use in Terminal. Indeed, some very useful applications are little more than an accessible front-end to various shell commands. AppleScript’s do shell script is one of its primary reasons for survival.

Of course Swift can execute other tasks or processes; for me the key question was how easily, and whether this was supported in a playground. As before, finding the answer depended on negotiating my way through the ‘documentation’. Most example code lags the current Xcode 8.1, Swift 3, and macOS 10.12 combination that are required for best playground support, so once again I had to convert from old versions.

swiftscript21

This was definitely made much more difficult by dreadful errors in Xcode’s Swift documentation. I was looking for what transpired to be an obsolete function in the NSTask class, within Foundation, which no longer exists. Swift playgrounds suggested that I tried the Process class, which I couldn’t find in the Swift docs because – in its alphabetical class list – Process appears between NSString and NSTextCheckingResult, as if it was still NSTask.

swiftscript22

And the contents of the class docs are also ambiguous, repeatedly referring to it as NSTask.

Once I had resolved this confusion, I came up with a playground example of an innocuous command call:
import Cocoa
import Foundation
var str = "Hello, playground"
let path = "usr/bin/say"
let task = Process.launchedProcess(launchPath: path, arguments: [str])
task.waitUntilExit()

where the final line ensures that you cannot run it again before it has completed. I think that this is actually easier than AppleScript’s do shell script, and it works fine in a playground.

swiftscript23

Droplet apps are another matter. In Swift practice, they have become windows or views onto which you drag and drop files for processing.

swiftpg12

Erica Sadun’s playground example is understandable: views like that in a playground are intended for just this sort of task. But similar examples appear in writing complete Swift apps, suggesting that they are viewed as being somehow normal and good. As any AppleScript user will know, a real droplet is the answer to so many tasks – a little app, often with no front end at all, which takes whatever you throw at it, from single docs to bulging folders, and chugs through, doing its job. A droplet can save users, and their employers, huge amounts of time and money, which would otherwise be wasted on repetitive tasks, like rescaling images, or sorting docs into different folders.

I even went back to Wilfredo Sánchez’s wonderful little DropScript app, which could turn anything executable at the command line into a droplet, but has lapsed into near-oblivion.

So for the moment, there is no simple answer in current Swift scripting to the droplet. I’m sure that someone competent in Swift could knock up a wrapper into which you could place code from a playground, but that doesn’t seem to have been done, yet.

The final task for the day was to look at whether Folder Actions are feasible. The answer here is more encouraging: yes, some not too frazzled code has been released by eonist to watch a designated folder, and trigger actions when anything changes within it, such as adding a new file. What’s more, this goes beyond current AppleScript Folder Actions, as it can detect more than just adding a file. I haven’t had a chance to test this yet, but it’s available on GitHub.

I’ve also been a little concerned as to how a script developed in Xcode’s playgrounds could actually be delivered to an ordinary user. With AppleScript, it is simple to save your completed script as a double-clickable app, and if you use Script Debugger you can now do much more, including signing the completed app. Playgrounds are convenient for the script developer, but if you’ve then got to recast your code into a full-blown Swift app, that is very inefficient.

One intriguing possibility is direct execution in the command line, detailed here by Hector Matos. For faceless apps this already seems possible, although I have not explored it fully yet. It does require the user’s Mac to have the Swift command line tools available, but could be a useful stopgap until a proper app wrapper is ready.