Blog

Swift Tip: Using AppKit from the Command-line

In Swift, it’s very easy to write a command-line app. As a simple example, you can paste the following into a file called wc.swift:

while let line = readLine() {
    print(line.split(separator: " ").count)
}

This will print out the number of words on each line. We can, for instance, use it on itself:

$ cat wc.swift | swift wc.swift
6
3
1

On the Mac, we’re not limited to console input and output. We can also run an entire Mac app directly from the command-line. When we import AppKit, we can access the shared application, set a delegate, and run the application.

For example, let’s save the following code into a file called spell.swift:

import AppKit

let app = NSApplication.shared

class AppDelegate: NSObject, NSApplicationDelegate {
    let window = NSWindow(contentRect: NSMakeRect(200, 200, 400, 200),
                          styleMask: [.titled, .closable, .miniaturizable, .resizable],
                          backing: .buffered,
                          defer: false,
                          screen: nil)

    func applicationDidFinishLaunching(_ notification: Notification) {
        window.makeKeyAndOrderFront(nil)
        let field = NSTextView(frame: window.contentView!.bounds)
        field.backgroundColor = .white
        field.isContinuousSpellCheckingEnabled = true
        window.contentView?.addSubview(field)
        DispatchQueue(label: "background").async {
            while let str = readLine(strippingNewline: false) {
                DispatchQueue.main.async {
                    field.textStorage?.append(NSAttributedString(string: str))
                }
            }
            app.terminate(self)
        }
    }
}

let delegate = AppDelegate()
app.delegate = delegate
app.run()

From the terminal, we can now run swift spell.swift, and the app will read lines from the standard input, adding them to a text field that has spell checking enabled. This particular example might be silly, but with all of AppKit at our disposal the possibilities are practically endless.

For instance, we could use the Open panel to prompt the user for a file:

// select.swift
import AppKit

func selectFile() -> URL? {
    let dialog = NSOpenPanel()
    dialog.allowedFileTypes = ["jpg", "png"]
    guard dialog.runModal() == .OK else { return nil }
    return dialog.url
}

print(selectFile()?.absoluteString ?? "")

Similarly, we could write scripts with a GUI for previewing images, processing images, finding a web page’s URL using WKWebView, and so on.

In other words, we can easily sprinkle some GUI elements onto our command-line scripts when it’s more convenient than relying on the command-line alone.

If you’d like to learn more, Swift Talk Episode 22 shows you how to write simple command-line tools that leverage frameworks like Foundation and Cocoa.

To watch, become a subscriber. 🙂

  • Watch the Full Episode

    Command Line Tools with Swift

    We show how we build simple command line tools leveraging the Cocoa frameworks. We use the Swift Package manager to include dependencies in our project.

    Episode 22 · October 28, 2016

Stay up-to-date with our newsletter or follow us on Twitter.

Back to the Blog

recent posts