Stephan
Stephan

Reputation: 16759

How to execute command after mac app opened

I want to execute a command after my app opens the system terminal app. I open the terminal app with the following:

let url = NSURL(fileURLWithPath: "/System/Applications/Utilities/Terminal.app", isDirectory: true) as URL
let configuration = NSWorkspace.OpenConfiguration()
NSWorkspace.shared.openApplication(at: url, configuration: configuration, completionHandler: { app, error in
    //app.executeMyCommand("echo hello")
})

And after it opened I want to execute the command "echo hello", as shown in the completionHandler. How can achieve this?

Upvotes: 4

Views: 639

Answers (3)

Willeke
Willeke

Reputation: 15598

Another solution:

Use NSAppleScript to execute an AppleScript:

let terminalScript = "echo hello"
/* oneliner * /
let AppleScriptSrc = "tell app \"Terminal\" to do script \"\(terminalScript)\""
/ **/
/* or a script */
let AppleScriptSrc = """
    tell app "Terminal"
        activate
        if number of windows > 0
            do script "\(terminalScript)" in tab 1 of window 1
        else
            do script "\(terminalScript)"
        end if
    end tell
"""
/**/
if let AppleScript = NSAppleScript(source: AppleScriptSrc) {
    var error: NSDictionary?
    AppleScript.executeAndReturnError(&error)
    if error != nil {
        print("Error: \(String(describing: error))")
    }
}

To allow sending Apple events:

  • under 'Signing & Capabilities', 'Hardened runtime', check 'Apple Events'
  • in info.plist add a line 'Privacy - AppleEvents Sending Usage Description'
  • in entitlements add a line 'com.apple.security.temporary-exception.apple-events' and item 'com.apple.Terminal'

Upvotes: 1

Willeke
Willeke

Reputation: 15598

Another solution:

Send a do script Apple event to Terminal:

let terminalScript = "echo hello"
if let url = NSWorkspace.shared.urlForApplication(withBundleIdentifier: "com.apple.Terminal") {
    let configuration = NSWorkspace.OpenConfiguration()
    let doScriptEvent = NSAppleEventDescriptor(eventClass: kAECoreSuite,
        eventID: kAEDoScript, targetDescriptor: nil, returnID: AEReturnID(kAutoGenerateReturnID),
        transactionID: AETransactionID(kAnyTransactionID))
    doScriptEvent.setParam(NSAppleEventDescriptor(string: terminalScript), forKeyword:keyDirectObject)
    configuration.appleEvent = doScriptEvent
    NSWorkspace.shared.openApplication(at: url, configuration: configuration, completionHandler: { app, error in
        if error != nil {
            print("Error: \(String(describing: error))")
        }
    })
}

To allow sending Apple events:

  • under 'Signing & Capabilities', 'Hardened runtime', check 'Apple Events'
  • in info.plist add a line 'Privacy - AppleEvents Sending Usage Description'
  • in entitlements add a line 'com.apple.security.temporary-exception.apple-events' and item 'com.apple.Terminal'

Upvotes: 5

Stephan Schlecht
Stephan Schlecht

Reputation: 27116

One possibility would be to use 'AppleScriptObjc' to control the terminal via an AppleScript from Swift.

Here a complete self-contained example:

ViewController.swift

import Cocoa
import AppleScriptObjC

class ViewController: NSViewController {

    var terminalBridge: TerminalBridging?

    override func viewDidLoad() {
        super.viewDidLoad()
        Bundle.main.loadAppleScriptObjectiveCScripts()
        let terminalBridgeClass: AnyClass = NSClassFromString("TerminalBridge")!
        self.terminalBridge = terminalBridgeClass.alloc() as? TerminalBridging
    }

    @IBAction func onExecute(_ sender: Any) {
        self.terminalBridge?.executeCommand("echo hello")
    }
    
}

TerminalBriding.swift

import Cocoa

@objc(NSObject) protocol  TerminalBridging {

    func executeCommand(_ cmd: String)
    
}

TerminalBridge.applescript

Note the suffix .applescript is important. Customize it to your preferences.

script TerminalBridge

    property parent : class "NSObject"

    to executeCommand:cmd
        tell application "Terminal"
            do script (cmd as text)
        end tell
    end executeCommand:

end script

Setup Project in Xcode

  • in XCode select the root node in the project navigator
  • then select the target and under 'Frameworks, Libraries, and Embedded Content' add 'AppleScriptObjC.framework' with defult 'Do Not Embed' setting
  • under 'Signing & Capabilities' remove 'App Sandbox' and add 'Hardened Runtime' instead
  • scroll down and under 'Resource Access' choose 'Apple Events'
  • in project navigator select 'Info.plist'
  • add 'NSAppleEventsUsageDescription' key with some useful explaination

Demo

On first run you would see:

Demo

If you press 'OK' an entry is added automatically to 'System Preferences/Security & Privacy/Privacy' on first run. Afterward the command is executed.

Terminal Screen Shot

Upvotes: 3

Related Questions