All Articles

Executing commands in the Terminal from Swift

I’ve been working on a little MacOS app which is supposed to spawn a Terminal and execute commands in it. As I was digging through Process and NSTask I wasn’t able to find a way to do both of these actions. However through some embedded AppleScript, you can get it to work.

The below code will achieve exactly that:

func execute(command: String) {
    let script = """
tell application \"Terminal\"
    do script "\(command)"
end tell
"""
    var error: NSDictionary?
    if let scriptObject = NSAppleScript(source: script) {
        let output = scriptObject.executeAndReturnError(&error)
        
        if (error != nil) {
            print("error: \(String(describing: error))")
        } else {
            print("output: \(String(describing: output.stringValue))")
        }
    }
}

If you now execute this, you’ll see an error similar to this in your console:

error: Optional({
    NSAppleScriptErrorAppName = Terminal;
    NSAppleScriptErrorBriefMessage = "Not authorized to send Apple events to Terminal.";
    NSAppleScriptErrorMessage = "Not authorized to send Apple events to Terminal.";
    NSAppleScriptErrorNumber = "-1743";
    NSAppleScriptErrorRange = "NSRange: {26, 55}";
})

In order to grant these permissions, add the following to your info.plist:

<key>NSAppleEventsUsageDescription</key>
<string>hello jeroen</string>

If you now trigger this behaviour again, you’ll be prompted with the following popup:

Permission request for Terminal interaction

Note that this will only work if you don’t have Sandbox enabled. In order to disable the Sandbox, delete your .entitlements file.