Xcode Swift playgrounds 2: Scripting files

After my frustrations displaying simple alerts and Open File dialogs in Xcode’s Swift playgrounds, I thought that I had better try something more constructive. This article starts to look at some key features that scripters need, and how accessible they are in Xcode’s Swift playgrounds.

First and foremost, scripts range in scale from quick-and-dirty few-liners to solve a simple problem, up to large multi-file projects. So the overhead, in terms of coding, effort, and skills, must also scale well. When I need to write a quick script to distribute files from one folder to several others, sorting by file extension, it should take a few minutes, and need just a few lines of code.

Little problems need little solutions, not a week of full-blown app development.

Swift is a highly versatile multi-paradigm language which can, for instance, be used as a functional language. But it also needs to work as a simple, imperative language, that starts at the first line of code and plods through to the end. So far, I see nothing in the Swift language to prevent that simple, linear style of coding, and lots of scope for tackling more complex problems using more advanced features of the language. The snag is that not all parts of macOS make that easy, so simple scripting is likely to need some simplifying wrappers for system calls.

Common little scripting tasks often deal with moving and copying files. This is one area where dealing with the macOS Foundation’s FileManager looks good, and a distinct improvement on AppleScript. Built long before OS X/macOS, AppleScript still deals with old file path conventions, horrible things like Macintosh HD:Users:jsmith:Documents:myfile.text. Feed that to something expecting a macOS POSIX path like /Users/jsmith/Documents/myfile.text and you have a nasty error. So AppleScript has to convert between the two.

Swift knows nothing about what happened in that halcyon past. You can moveItem(at: URL, to: URL) using URLs, or moveItem(atPath: String, toPath: String) using POSIX paths. FileManager is indeed the scripter’s best friend: want the contents of a complete file? Then just contents(atPath: String) will deliver it. Want to see if two files have the same contents? Then contentsEqual(atPath path1: String, andPath path2: String) will tell you, true or false.

If you do much scripting with files, have a browse through the documentation for FileManager and you be very happy indeed to be scripting with Swift. Yes, there’s also good support for iCloud-based items.

There is a catch, of course. At present, you have to fart about with some niceties before a playground will allow you to use these. As ever, this is not exactly clearly documented anywhere, but I discovered that, for example,
import Cocoa
import Foundation
var str = "Hello, playground"
let FM = FileManager.default
do {
try FM.attributesOfItem(atPath: "/Users/hoakley/Documents")
} catch let error as NSError {
print("Error: \(error.domain)")
}

works very nicely, and returns a great long list of the attributes of the item at the POSIX path which you feed it.

swiftpg11

Of course most of the examples in books are likely to lead you to
let FM = NSFileManager()
as the initialisation, which fails on two counts. Swift playgrounds helpfully tells you one of them, that NSFileManager has been renamed FileManager, so you then try
let FM = FileManager()
which fails on the other count, which Swift playgrounds won’t tell you about, but it’s the wrong form of initialisation, and just returns an inscrutable error when you try running it.

swiftpg12

I have also been skimming through Erica Sadun’s superb book on Swift playgrounds, which is invaluable. There is a catch here too: as an ace Swift coder, she uses powerful language features to structure her playgrounds. She has one playground, written in up-to-date Swift 3, which works beautifully, and counts the number of words in text files which you drop onto its ‘catch’ area. It is elegantly implemented in a style which might make many scripters turn and run for AppleScript. I will leave you with screenshots of its code, as an exercise in understanding Swift.

swiftpg13

swiftpg14

If you thought that AppleScript’s droplet harness was a pain, then you might want something more direct in Swift scripting.

Essential reading

Erica Sadun, Playground Secrets and Power Tips, Apple iBooks store.
Apple, Using Swift with Cocoa and Objective-C, free from Apple iBooks store.