How do I run AppleScript Safely inside my Swift app?

Hey there!

I have a Swift app using SwiftUI, and I want to run some AppleScript Safely inside it.

Here is my current code:

func example() {
        var script = NSAppleScript(source: """
        tell application "Music"
            play
            repeat with vlm from 0 to 100 by 1
                set the sound volume to vlm
                delay \(fadetime / 100)
            end repeat
        end tell
        """)
        DispatchQueue.global(qos: .background).async {
            let success = script?.compileAndReturnError(nil)
            assert(success != nil)
            print(success)
        }
    }

However, it does not do anything. I have tried using process, however, that simply errors out in -600 "The application is not running." Is there something I am missing here?

Thank you!

Answered by DTS Engineer in 754722022

Your code calls compileAndReturnError(_:), which only compiles the script. If you want it to run, call executeAndReturnError(_:).

Remember that AppleScript is based on Apple events, so you’ll need permission to send Apple events, meaning:

Finally, don’t use the Dispatch global concurrent queue. See Avoid Dispatch Global Concurrent Queues.


Oh, one last thing. executeAndReturnError(_:) is incorrectly annotated in Objective-C, so Swift things the result is not optional. To get around this, immediately cast the result to an optional. See this post for an example.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Accepted Answer

Your code calls compileAndReturnError(_:), which only compiles the script. If you want it to run, call executeAndReturnError(_:).

Remember that AppleScript is based on Apple events, so you’ll need permission to send Apple events, meaning:

Finally, don’t use the Dispatch global concurrent queue. See Avoid Dispatch Global Concurrent Queues.


Oh, one last thing. executeAndReturnError(_:) is incorrectly annotated in Objective-C, so Swift things the result is not optional. To get around this, immediately cast the result to an optional. See this post for an example.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Good question and informative, helpful answer. I've two follow up questions:

  1. Will using Apple Script inside the app create any challenges related to selling the app via the AppStore?

  2. Is it better to use Scripting Bridge (SB) framework? I reckon SB would enable communicating with the scriptable app in a more robust manner?

    a) What are the deployment / entitlement related considerations to consider when using SB instead of the Apple Script?

Hi @konradedgar & eskimo. I am still running into issues regarding permissions and still receive -600 "The application is not running" error.

Can confirm I have Hardened Runtime enabled with Apple Events, and added the NSAppleEventsUsageDescription.

The script I am trying to execute is quite simple and works when running it in the AppleScript app.

Any recommendations...? Also curious about the answers to @konradedgar follow up questions!

Swift

func openTrashDirectory() {
    let script = """
        tell application "Finder"
            open trash
            activate
        end tell
        """
    
        DispatchQueue.main.async {
            var error: NSDictionary?
            if let scriptObject = NSAppleScript(source: script) {
                scriptObject.executeAndReturnError(&error)
                if let error = error {
                    print("Error: \(error)")
                }
            }
        }
    }
How do I run AppleScript Safely inside my Swift app?
 
 
Q