Using Applescript with Apple Events in MACos - Script Not Working

Using AppleScript with Apple Events in macOS - Script not working

The problem with this code — which is an incredibly un-obvious problem, mind you — is that you're using code meant to run a script handler (a method or subroutine) to try to run the full script. One of the oddnesses of Obj-C's AppleScript classes is that there is no easy way to run a script with parameters, so the workaround is to enclose the code to be executed within a script handler, and use an Apple Event that calls that handler. To make your code work, you'll do something like the following...

First, change the script so that the code is in a handler:

var script: NSAppleScript = { 
let script = NSAppleScript(source: """

-- This is our handler definition
on sendMyEmail(theSubject, theContent, recipientName, recipientAddress, attachmentPath)
tell application "Mail"

-- Create an email
set outgoingMessage to make new outgoing message ¬
with properties {subject:theSubject, content:theContent, visible:true}

-- Set the recipient
tell outgoingMessage
make new to recipient ¬
with properties {name:recipientName, address:recipientAddress}

make new attachment with properties {file name:POSIX file attachmentPath}

-- Mail.app needs a moment to process the attachment, so...
delay 1

-- Send the message
send
end tell
end tell
end sendMyEmail
"""
)!

Then alter the Apple Event you construct so that it passes the parameters and calls the handler we just defined:

func runScript() {
let parameters = NSAppleEventDescriptor.list()
parameters.insert(NSAppleEventDescriptor(string: "Some Subject"), at: 0)
parameters.insert(NSAppleEventDescriptor(string: "Some content of the email"), at: 0)
parameters.insert(NSAppleEventDescriptor(string: "Some Name"), at: 0)
parameters.insert(NSAppleEventDescriptor(string: "someone@example.com"), at: 0)
parameters.insert(NSAppleEventDescriptor(string: attachmentFileURL.path), at: 0)

let event = NSAppleEventDescriptor(
eventClass: AEEventClass(kASAppleScriptSuite),
eventID: AEEventID(kASSubroutineEvent),
targetDescriptor: nil,
returnID: AEReturnID(kAutoGenerateReturnID),
transactionID: AETransactionID(kAnyTransactionID)
)

// this line sets the name of the target handler
event.setDescriptor(NSAppleEventDescriptor(string: "sendMyEmail"), forKeyword: AEKeyword(keyASSubroutineName))

// this line adds the parameter list we constructed above
event.setDescriptor(parameters, forKeyword: AEKeyword(keyDirectObject))

var error: NSDictionary? = nil
_ = self.script.executeAppleEvent(event, error: &error) as NSAppleEventDescriptor?

print ("runScript",self.script)

}
}

If you don't need to pass parameters, you could run the script directly using script.executeAndReturnError(&error), but if you need to pass parameters, you'll need to use this 'handler' approach.

Get Apple Events from AppleScript

Yes, there are several such ways. My favorite is to use Script Debugger, which just translates the AppleScript into raw Apple events for you. Alternatively, you can run your AppleScript in an environment where export AEDebugSends=1; export AEDebugReceives=1 has been turned on.

AppleScript used in my cocoa mac app, stopped working in osx 10.14

You say "it worked perfectly" in previous systems. I find that difficult to believe, since almost everything about your code is wrong. I corrected your code and got your script to work, with very little difficulty.

I'll try to describe what I did.

To prepare the ground, I ran a version of your script in Script Editor (removing the backslashes and string interpolation of course):

tell application "Safari"
activate
end tell
tell application "System Events"
tell process "Safari.app"
keystroke "c" using {command down}
delay 0.1
set myData to (the clipboard) as text
return myData
end tell
end tell

The script didn't run at first, but a dialog sent me to System Preferences -> Security & Privacy -> Accessibility, where I checked Script Editor and System Events.

Sample Image

Now I was ready to create the app. My app is called AnotherAppleScriptExample. In its entitlements, sandboxing is NO.

Sample Image

In its Info.plist is this entry:

Sample Image

My version of your code (fixing the various Swift mistakes) is this:

        let app = "Safari.app"
let script = """
tell application "\(app)"
activate
end tell
tell application "System Events"
tell process "\(app)"
keystroke "c" using {command down}
delay 0.1
set myData to (the clipboard) as text
return myData
end tell
end tell
"""
if let scriptObject = NSAppleScript(source: script) {
var error: NSDictionary? = nil
let result = scriptObject.executeAndReturnError(&error)
if( kAENullEvent != result.descriptorType ){
print(result.stringValue as Any)
}
}

I ran the app. I got two dialogs. First this:

Sample Image

I clicked OK. Then this:

Sample Image

I clicked Open System Preferences. In System Preferences, I checked my app (now both System Events and my app are checked):

Sample Image

Now the ground is fully prepared. I quit the app and ran it again. The script worked correctly, printing the selection in Safari.

How to run a system event AppleScript from a hardened macOS app?

After much gathering at hints all over the internet, I've come to the conclusion that the problem lies in having different binaries.

It makes sense that macOS does some binary check to make sure the application has not been swaped. I had therefore two binaries in my machine a "release" one, which was DeveloperID signed. And the XCode debug one, which uses a development certificate. So both are different and therefore do not pass the binary check.

The brute force solution is not to have a release version installed in my machine. Another solution would be to have the debug binary have a different bundle ID (com.ospfranco.sol), so that macOS allows both the release binary and the debug binary to have accessibility access.

Running AppleScript from Swift not working

This error occurs if the application is sandboxed.

You have two options:

  1. Add the com.apple.security.temporary-exception.apple-events entitlement for System Events
  2. Put the script in the Application Scripts folder of the application and run it from there with NSUserAppleScriptTask


Related Topics



Leave a reply



Submit