You’re right that there’s a bunch of different ways to do this. The two that I recommend are:
NSUserScriptTask
, if you want to make a general script attachment mechanismNSAppleScript
, if you want lots of control
The former has the advantage that it’s compatible with the App Sandbox.
Implementing the latter is a bit tricky if you’re not deeply familiar with the whole history of Apple events and AppleScript. Here’s some snippets to get you started.
First, here’s how to construct an
NSAppleScript
from source.
var script: NSAppleScript = {
let script = NSAppleScript(source: """
on displayMessage(message)
tell application "Finder"
activate
display dialog message buttons {"OK"} default button "OK"
end tell
end displayMessage
"""
)!
let success = script.compileAndReturnError(nil)
assert(success)
return script
}()
Note that I’m storing this in a property. I do this because compiling scripts is relatively expensive, so it’s best to do it once and cache the results.
Here’s how how run that the
displayMessage
handler in that script, passing it a parameter.
let parameters = NSAppleEventDescriptor.list()
parameters.insert(NSAppleEventDescriptor(string: "Hello Cruel World!"), at: 0)
let event = NSAppleEventDescriptor(
eventClass: AEEventClass(kASAppleScriptSuite),
eventID: AEEventID(kASSubroutineEvent),
targetDescriptor: nil,
returnID: AEReturnID(kAutoGenerateReturnID),
transactionID: AETransactionID(kAnyTransactionID)
)
event.setDescriptor(NSAppleEventDescriptor(string: "displayMessage"), forKeyword: AEKeyword(keyASSubroutineName))
event.setDescriptor(parameters, forKeyword: AEKeyword(keyDirectObject))
var error: NSDictionary? = nil
let result = self.script.executeAppleEvent(event, error: &error) as NSAppleEventDescriptor?
There’s some tricky things to note here:
In line 2 I pass 0 to the
at
parameter, which indicates that the value should be added to the end of the list.There are lots of type conversions (lines 5, 6, 8 and 9) because Swift is importing the constants with the wrong type (because they’re anonymous enums in the Objective-C header).
In line 15 I cast the result of
executeAppleEvent(_:error:)
to NSAppleEventDescriptor?
because of an annotation bug in the Objective-C header. The method is documented to return nil
on error but the header doesn’t indicate that (r. 38702068).
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"