More fun scripting with Swift and Xcode: Swift Documentation Markup

If you don’t believe how important documenting your code is, try writing some APL without comments or documentation, leave it for a few days, then come back to it. Sometimes it’s actually easier to write the code again than it is trying to understand the code that you wrote a few days ago.

It’s also telling that some of the longest-lasting working code, written by Professor Don Knuth for TeX, used WEB, his own self-documenting code system.

I was therefore delighted to discover that Swift supports structured documentation, using a mixture of XSL style sheets and CommonMark markup (a standard form of Markdown). Comment your code properly and Xcode’s Quick Help will give ready access to your documentation. You can use this with most symbols, including functions, methods, local constants, variables, types, protocols, and much else besides.

This is documented in various Swift docs, but I strongly recommend anyone using Swift seriously to buy Erica Sadun’s superb account of it in her iBook Swift Documentation Markup, from the iBook Store. That is what I have been working from, and will continue to use as my reference.

Viewing Quick Help in Xcode 8 is simple, and accessible in several ways. My screenshots are taken from the Quick Help inspector, simply by clicking within the symbol in my source code. You can also view the information as an overlay by Opt-clicking (or an equivalent, such as a three-finger tap) on the symbol, if you prefer.

scripting91

If you don’t want to use the full features available, and just want to add quick descriptions to symbols, you can do this by adding comments using either
/// for a single line, or
/**
and
*/
to bracket multi-line comments. Place these immediately before your symbol definition, and Quick Help should pick it up.

scripting92

If you want to make full use of the features, place your cursor at the start of the line in which the symbol is defined, e.g. before func, and use the Editor / Structure / Add Documentation menu command. This will then insert a comment block immediately before that symbol definition, in which you can fill the blanks.

scripting93

Here are three examples of the actual comments I have inserted into my collection of useful functions for scripting, together with the formatted output which they produce.

Using the basic template is an excellent start, and very quick to accomplish:

/// Obtains the short version number for a bundle.
///
/// - Parameter path: a String containing the POSIX path to the bundle.
/// - Returns: a String containing the short version number of the bundle.
func getBundleVersion(path: String) -> String

scripting94

The next two illustrate additional markup features which are commonly used:

/// Runs a shell command with its arguments, capturing standard output.
///
/// *See also* `doShellScriptWithPrivileges` which runs with elevated privileges.
/// - Note: You cannot use `sudo` in this func to elevate privileges.
///
/// - Parameters:
/// - launchPath: a String containing full path to the tool to be run, e.g. `/usr/bin/spctl`
/// - arguments: array of String arguments including command line options.
///
/// Note that each element in that array contains a single command line option.
/// - Returns: 2-element array containing:
/// - if successful, stdout and an empty string,
/// - if unsuccessful, an empty string and an error message.
func doShellScript(launchPath: String, arguments: [String]?) -> [String]

scripting95

/// Runs a shell command *with elevated privileges,* displaying an authentication dialog.
///
/// *See also* `doShellScript` which runs with normal privileges.
/// - Warning: Currently this calls AppleScript to execute the command,
/// which is inefficient and potentially hazardous.
///
/// - Parameter source: a String containing the command line to be run (without `sudo`).
/// - Returns: a String containing:
/// - `ERROR`, if the command failed or was cancelled by the user,
/// - the returned text from running the command, if successful.
func doShellScriptWithPrivileges(source: String) -> String

scripting96

You should be able to work out the structural components, such as Returns:, and the embedded markup, such as `…` for code and *…* for italics.

Not everything works in every release of Xcode. I was a little disappointed to discover that the SeeAlso tag refused to work in the current release, but have worked around that as you can see above.

Surprisingly, few other books or manuals on Swift 3 mention these documentation features, and when they do, they are very brief. No wonder most of us don’t document adequately.