Programmatically passing files to Final Cut via Apple Events

We have trying to programmatically send data to Final Cut Pro by using Apple Event as decribed in Sending Data Programmatically to Final Cut Pro :

tell application "Final Cut Pro"
    activate
    open POSIX file "/Users/JohnDoe/Documents/UberMAM/MyEvents.fcpxml"
end tell

This works fine in Script Editor but we run into problems when trying to do the same in our macOS app.

We found interesting information in Workflow Extensions SDK 1.0.2 Release Notes.pdf.

A) Hardened runtime has "Apple Events Enabled" checked.

B) Info.plist contains NSAppleEventsUsageDescription:

<key>NSAppleEventsUsageDescription</key>
<string>Test string</string>

C) We added following entitlements:

<key>com.apple.security.scripting-targets</key>
<dict>
    <key>com.apple.FinalCut</key>
    <array>
        <string>com.apple.FinalCut.library.inspection</string>
    </array>
     <key>com.apple.FinalCutTrial</key>
    <array>
        <string>com.apple.FinalCut.library.inspection</string>
    </array>
</dict>
<key>com.apple.security.automation.apple-events</key>
<true/>

With this configuration in place, our app is able to call AppleScript to activate Final Cut Pro application but it is unable to open the file. Following error is returned:

Error executing AppleScript: {
    NSAppleScriptErrorAppName = "Final Cut Pro Trial";
    NSAppleScriptErrorBriefMessage = "A privilege violation occurred.";
    NSAppleScriptErrorMessage = "Final Cut Pro Trial got an error: A privilege violation occurred.";
    NSAppleScriptErrorNumber = "-10004";
    NSAppleScriptErrorRange = "NSRange: {56, 64}";
}

Also there is no prompt asking user to allow Automation from our app to Final Cut. I am not sure whether the prompt is to be expected when developing an application in Xcode.

Our current workaround is to add (or even replace com.apple.security.scripting-targets with): com.apple.security.temporary-exception.apple-events entitlement like this

  <key>com.apple.security.temporary-exception.apple-events</key>
  <array>
    <key>com.apple.FinalCutTrial</key>
  </array>

However while this approach might work in development we know this would probably prevent us from publishing the app to Mac App Store.

I think we are missing something obvious. Could you help? :-)

Answered by DTS Engineer in 788936022

I don’t think there’s a way to do this from a sandboxed app. In a non-sandboxed app you can send kAEOpenDocuments ('odoc') Apple events as you wish. However, the App Sandbox restricts that path, on the assumption that apps will use the facilities provided by NSWorkspace.

This isn’t an AppleScript-specific thing; if you use Apple events to send the 'odoc' event directly, you bump into the same restriction. For example, the code snippet below works in a non-sandboxed app but fails in a sandboxed one.

I recommend that you file a bug against that doc, requesting that it call out that NSWorkspace is required for a sandboxed app. Please post your bug number, just for the record.

Share and Enjoy

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

func openInTextEditAE(_ urls: [URL]) {
    do {
        print("will open")
        let target = NSAppleEventDescriptor(bundleIdentifier: "com.apple.TextEdit")
        let ae = NSAppleEventDescriptor(
            eventClass: AEEventClass(kCoreEventClass),
            eventID: AEEventID(kAEOpenDocuments),
            targetDescriptor: target,
            returnID: AEReturnID(kAutoGenerateReturnID),
            transactionID: AETransactionID(kAnyTransactionID)
        )
        let directObject = NSAppleEventDescriptor.list()
        for url in urls {
            directObject.insert(NSAppleEventDescriptor(fileURL: url), at: 0)
        }
        ae.setParam(directObject, forKeyword: keyDirectObject)
        _ = try ae.sendEvent(options: [.waitForReply], timeout: 5.0)
        print("did open")
    } catch {
        print("did not open, error: \(error)")
    }
}

Is your app sandboxed?

If you move MyEvents.fcpxml to the home directory and then use /Users/JohnDoe/MyEvents.fcpxml, does that fix things?

Share and Enjoy

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

Hey Eskimo. Good to see you in this thread! :-)

Moving the file to /Users/JohnDoe/MyEvents.fcpxml does not help. Same error still occurs.

Application is sandboxed (the default configuration when you create a new XCode project). The app in its current test form does not interact with the file at all. It just tries to tell Final Cut Pro to open it.

Here is the most simple version of the code we are trying to make function:

struct ContentView: View {
    var body: some View {
        VStack {
            Button {
                callScript()
            } label: {
                Text("Open file in Final Cut")
            }
        }
        .padding()
    }
}

func callScript(){
    let scriptSourceToSend = """
tell application "Final Cut Pro Trial"
    activate
    open POSIX file "/Users/user/somefile.fcpxml"
end tell
"""
    let script = NSAppleScript(source: scriptSourceToSend)
    var error: NSDictionary? = nil
    let result = script?.executeAndReturnError(&error)
    if let error = error {
        print("Error executing AppleScript:", error)
    } else {
        print("XML file imported successfully.")
    }
    if result == nil { }
}

The app in its current test form does not interact with the file at all.

Ah, interesting.

Normally when you pass a file reference via an Apple event the system adds a sandbox extension so that the destination app can access it. It won’t be able to do that if your app doesn’t have access to the file in the first place.

If you temporarily disable App Sandbox on your app, does that help?

Share and Enjoy

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

If you temporarily disable App Sandbox on your app, does that help?

Yes, when I disable app Sandbox completely, there is no privilege validation error

Normally when you pass a file reference via an Apple event

I would assume so for the native file object. I did not know this was the case for POSIX file as well.

I also tried to put the file into ~/Movies and in Targets -> Signing & Capabilities -> App Sandbox -> File Access -> Set Movies folder to Read/Write. This still produces the privilege violation error.

when I disable app Sandbox completely, there is no privilege validation error

OK. Do you need sandboxing to be enabled? If not, disabling it is clearly the easiest path forward (-:

Share and Enjoy

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

Do you need sandboxing to be enabled? If not, disabling it is clearly the easiest path forward

Well.. I don't but Apple does. At least for the Mac App Store :-)

Say we want to distribute Workflow plugin for FInal Cut Pro in Mac App Store. WeI need to comply with requirements (sandboxing) or get some sort of exception. I hear exceptions can be very hard to get.

I don't but Apple does.

*chuckle*

Say we want to distribute Workflow plugin for FInal Cut Pro in Mac App Store. WeI need to comply with requirements (sandboxing) or get some sort of exception.

I’m not super familiar with these plug-ins. Are they loaded within the Final Cut Pro app process? Or are they built as an app extension?

I hear exceptions can be very to get.

That’s my understanding as well.

Share and Enjoy

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

I’m not super familiar with these plug-ins. Are they loaded within the Final Cut Pro app process? Or are they built as an app extension?

You can read more about them here https://developer.apple.com/documentation/professional_video_applications/workflow_extensions

However the problem with sending Apple Events and Sandboxing is not directly related to Final Cut extensions. Right now we are just trying out the basic process with a generic macOS app created via Xcode template where we (try) to follow the instructions from Apple and make it work.

What would you recommend we do next? Should we open a new DTS ticket about this specific usage of AppleEvents Vs. App Sandboxing?

Accepted Answer

I don’t think there’s a way to do this from a sandboxed app. In a non-sandboxed app you can send kAEOpenDocuments ('odoc') Apple events as you wish. However, the App Sandbox restricts that path, on the assumption that apps will use the facilities provided by NSWorkspace.

This isn’t an AppleScript-specific thing; if you use Apple events to send the 'odoc' event directly, you bump into the same restriction. For example, the code snippet below works in a non-sandboxed app but fails in a sandboxed one.

I recommend that you file a bug against that doc, requesting that it call out that NSWorkspace is required for a sandboxed app. Please post your bug number, just for the record.

Share and Enjoy

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

func openInTextEditAE(_ urls: [URL]) {
    do {
        print("will open")
        let target = NSAppleEventDescriptor(bundleIdentifier: "com.apple.TextEdit")
        let ae = NSAppleEventDescriptor(
            eventClass: AEEventClass(kCoreEventClass),
            eventID: AEEventID(kAEOpenDocuments),
            targetDescriptor: target,
            returnID: AEReturnID(kAutoGenerateReturnID),
            transactionID: AETransactionID(kAnyTransactionID)
        )
        let directObject = NSAppleEventDescriptor.list()
        for url in urls {
            directObject.insert(NSAppleEventDescriptor(fileURL: url), at: 0)
        }
        ae.setParam(directObject, forKeyword: keyDirectObject)
        _ = try ae.sendEvent(options: [.waitForReply], timeout: 5.0)
        print("did open")
    } catch {
        print("did not open, error: \(error)")
    }
}

Thank you so much Eskimo!

I have submitted FB13816400 about the documentation page as you suggested.

I also confirm using NSWorkspace works with Sandboxed app. Here is super simple stupid code example:

   let fileURL = [URL(fileURLWithPath: "/Users/user/Movies/test.fcpxml")]
    let appURL = URL(fileURLWithPath: "/Applications/Final Cut Pro Trial.app")
    let conf = NSWorkspace.OpenConfiguration()
    NSWorkspace.shared.open(fileURL, withApplicationAt: appURL, configuration: conf)
Programmatically passing files to Final Cut via Apple Events
 
 
Q