6

I'm making a simple menubar app to do some actions such as toggling dark mode in Mojave

Writing an AppleScript for it is fine and when it works 100% when directly running the script. In order to implement it into a menu bar app, I tried searching online and the only answer I found was in this link: Can you execute an Applescript script from a Swift Application

However, when I tried the first solution, I got an error in the console saying:

script: /Users/username/Documents/myscript.scpt: Operation not permitted

My code looked like this:

let proc = Process()
proc.launchPath = "/usr/bin/script"
proc.arguments = ["/Users/username/Documents/myscript.scpt"]
proc.launch()

I've tried another solution, which this time uses NSAppleScript:

let myAppleScript = "tell application \"System Events\" \ntell appearance preferences to set dark mode to not dark mode \nend tell"
var error: NSDictionary?
if let scriptObject = NSAppleScript(source: myAppleScript) {
    if let output: NSAppleEventDescriptor = scriptObject.executeAndReturnError(&error) {
        print(output.stringValue)
    } else if (error != nil) {
        print("error: \(error)")
    }
}

However this time I get an error saying:

error: Optional({
    NSAppleScriptErrorAppName = "System Events";
    NSAppleScriptErrorBriefMessage = "Application isn\U2019t running.";
    NSAppleScriptErrorMessage = "System Events got an error: Application isn\U2019t running.";
    NSAppleScriptErrorNumber = "-600";
    NSAppleScriptErrorRange = "NSRange: {86, 9}";
})

But if I change the script to something simpler, such as "display notification \"test\"", the second solution works perfectly.

This means that the second solution runs AppleScripts fine, but it can't seem to call System Events. How do I fix this?

akmin04
  • 677
  • 6
  • 14
  • "However, when I tried the first solution, I got an error in the console saying" You aren't supposed to access the path there without user's consent. – El Tomato Oct 02 '18 at 03:34
  • Alright that makes sense. Is there a way to access it or is that solution a dead end? – akmin04 Oct 02 '18 at 04:49

3 Answers3

8

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
vadian
  • 274,689
  • 30
  • 353
  • 361
  • The `Application Scripts` folder is intended for users' scripts only; your app can read from it but not write to it. Scripts that are part of your app should be embedded in the app bundle and use `NSAppleScript` (sufficient for trivial tasks) or [AppleScript-ObjC](https://github.com/hhas/Swift-AppleScriptObjC) to call them, and declare the necessary sandbox entitlements. That protects everyone. macOS's security model may be a clunky PITA, but the correct solution to that is to file Radar requests, not try to subvert it; which will only earn you a rightful AppStore rejection. – foo Oct 03 '18 at 11:49
  • @foo, it seems that both NSAppleScript and OSAScript have memory leaks, so I don't use either of them anymore. I call osascript using a function that I wrote called bash() I imagine that osascript may leak too but it doesn't matter since the whole process is run and then goes away automatically deallocating everything that it allocated. bash() creates a process using the Process class in Swift. – Kaydell Oct 05 '18 at 17:41
  • I think something with the sandboxing isn't correct for #1, Vadian. I added com.apple.systemevents and com.apple.applescript to my entitlements under the temporary exceptions as you stated and I'm still getting the "execution error: System Events got an error: Application isn’t running. (-600)" error. Am I missing something obvious? – ajc6432 Dec 16 '18 at 08:49
0

First disable "App Sandboxing" in Xcode and then try Verified Answer by CRGreen

meMadhav
  • 233
  • 2
  • 12
0

I encountered this error due to my app's being sandboxed, despite having sandboxing off in some places. Disabling sandboxing took me several steps, and I'm not sure which were necessary. I tried most of the things listed in this question.

It seems like these were (at minimum) the two necessary steps:

1. Disable sandbox in the app entitlements file

This can be done in Xcode, by going to the file with the gold checkmark/star icon, and clicking "NO" in the dropdown for "App Sandbox"

Alternatively, this can be done by editing the app's .entitlements file to include:

<key>com.apple.security.app-sandbox</key>
<false/>

2. Add an Apple Events sending usage description to the Info.plist file

Again, this can be done in two ways, either in Xcode, by navigating to the Info file, adding the key Privacy - AppleEvents Sending Usage Description, and putting in a string to request access (afaik this can be any string).

Alternatively, this can be done by editing the app's Info.plist file to include:

<key>NSAppleEventsUsageDescription</key>
<string>(your request string here)</string>

Either way, after changing both these settings (and possibly more, I did click a lot of buttons), I then had to rebuild the app, trigger the AppleScript again, and get a prompt to allow access. This led me to the Privacy and Security>Accessibility pane of System Settings, where I could then flip the toggle to grant my app Accessibility features.

Thanks in particular to this answer for helping me figure this out!

Chris McElroy
  • 161
  • 11