How to enable AppleScript between Parent and Child applications within sandbox for Mac App Store?

I have a parent app that contains a child app in the bundle. The child app uses applescript to trigger some events in the parent app.

Now that I'm preparing for App Store I have sandboxed the apps and I am missing something with the entitlements that I don't understand.

In the parent app I have defined the .sdef and I can dump it with sdef /Applications/Parent.app once installed from TestFlight and I see:


<!DOCTYPE dictionary SYSTEM "file://localhost/System/Library/DTDs/sdef.dtd">

<dictionary title="Parent">

    <suite name="Parent Suite" code="prnt" description="Parent Scripts">

        <command name="is_first_run" code="prntisfr" description="Is this the first time running the app?">
            <cocoa class="MyScriptInterface"/>
        </command>

        <command name="activation_complete" code="prntWRac" description="Activation is complete">
            <cocoa class="MyScriptInterface"/>
        </command>

        <command name="sign_out" code="prntWRso" description="Sign out and delete local credentials">
            <cocoa class="MyScriptInterface"/>
        </command>

        <command name="get_version" code="prntgetv">
            <cocoa class="MyScriptInterface"/>
            <direct-parameter type="text" description="None"/>
            <result type="text"/>
        </command>

    </suite>

</dictionary>

In the parent app I have the following included in the .plist:

<key>NSAppleEventsUsageDescription</key>

        <string>AppleEvents needed to communicate between components</string>

        <key>NSAppleScriptEnabled</key>

        <true/>
        <key>OSAScriptingDefinition</key>

        <string>Parent.sdef</string>

In the Parent entitlements I include the following key parts:

<key>com.apple.security.automation.apple-events</key>
        <true/>
<key>com.apple.security.scripting-targets</key>
        <dict>
                <key>com.foo.parent</key>
                <array>
                        <string>com.foo.parent.is_first_run</string>
                     <string>com.foo.parent.activation_complete</string>
                        <string>com.foo.parent.sign_out</string>
                        <string>com.foo.parent.get_version</string>
                </array>

                <key>com.foo.parent.child</key>
                <array>
                        <string>com.foo.parent.is_first_run</string>
                        <string>com.foo.parent.activation_complete</string>
                        <string>com.foo.parent.sign_out</string>
                        <string>com.foo.parent.get_version</string>
                </array>

        </dict>

In the Child app I have only this key:

<key>NSAppleScriptEnabled</key>
        <true/>

And in the Child entitlements I have only this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

<plist version="1.0">
<dict>
        <key>com.apple.security.app-sandbox</key>
        <true/>

        <key>com.apple.security.inherit</key>
        <true/>
</dict>
</plist>

I am not getting something. When I install the app and watch Console output I see a combination of errors:

For the Parent app:

AppleEvents/sandbox: Returning errAEPrivilegeError/-10004 and denying dispatch of event prnt/isfr from process '<private>'/0x0-0x7dccdc5, pid=95806, because it is not entitled to send an AppleEvent to this process.

For the Child app from sandboxd:


Violation:       deny(1) appleevent-send com.apple.systemevents

MetaData: {"platform-binary":false,"build":"macOS 12.3.1 (21E258)","sandbox_checker":"appleeventsd","process-path":"\/Applications\/Parent.app\/Contents\/MacOS\/Child.app\/Contents\/MacOS\/Child","profile-in-collection":false,"platform_binary":"no","primary-filter-value":"com.apple.systemevents","primary-filter":"appleevent-destination","checker":"appleeventsd","platform-policy":false,"policy-description":"Sandbox","summary":"deny(1) appleevent-send com.apple.systemevents","binary-in-trust-cache":false,"responsible-process-team-id":"367******2","target":"com.apple.systemevents","hardware":"Mac","pid":95806,"appleevent-destination":"com.apple.systemevents","flags":5,"responsible-process-signing-id":"com.foo.parent","apple-internal":false,"normalized_target":["com.apple.systemevents"],"checker-pid":359,"profile-flags":0,"operation":"appleevent-send","errno":1,"action":"deny","uid":501,"responsible-process-path":"\/Applications\/Parent.app\/Contents\/MacOS\/Parent","signing-id":"com.foo.parent.child","team-id":"367******2","container":"\/Users\/spartygw\/Library\/Containers\/com.foo.parent\/Data","process":"Child","release-type":"User"}

I should've included how I'm attempting to execute applescript from the Child app:

NSDictionary* errorDict; NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource: @"tell application id \"com.foo.parent\" to sign_out"]; NSAppleEventDescriptor* returnDescriptor = [scriptObject executeAndReturnError: &errorDict]; Maybe I can only execute compiled applescripts?

Apple Script used to be completely forbidden in the Mac App Store. From what I understand, there is some limited usage available with Mail and possibly 3rd party apps. You seem to be doing everything correctly according to my limited understanding of the documentation.

However, your console output specifically says "com.apple.systemevents", which would definitely be a forbidden target. Maybe it would work if you tried the scripting bridge directly.

Why bother with AppleScript at all anyway? There are lots more convenient ways to communicate between a parent and helper, such as XPC.

I have a parent app that contains a child app in the bundle. The child app uses applescript to trigger some events in the parent app.

In this setup how is the child app started?

Share and Enjoy

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

I tried to post a comment to Quinn's question but the code formatting for comments is ugly. Here is how I'm starting the child app:

let process = Process()
let url = Bundle.main.bundleURL.path + "/Contents/MacOS/Child.app/Contents/MacOS/Child"

process.executableURL = URL(fileURLWithPath:url)
process.terminationHandler = { (process) in
    print("\ndidFinish: \(!process.isRunning)")
}
do {
    try process.run()
} catch let error as NSError {
    NSLog("Failed to execute ", error)
}

I tried to post a comment to Quinn's question but the code formatting for comments is ugly.

Yep. Sorry about that. Comments aren’t meant for full-fledged replies, despite the UI misleading you into thinking that they are )-: This is something that we intend to fix (r. 80839588).

Here is how I'm starting the child app:

Oh, interesting, it really is a child process.

Note Hardcoding /Contents/MacOS/Child.app/Contents/MacOS/Child is kinda bogus. I’ve posted a better alternative below. However, I can guarantee you that this isn’t the cause of your Apple event problem.

I’d like to take a step back and ask why you’re using Apple events for this IPC? There are plenty of other IPC options and combining Apple events and sandboxing is tricky.


nk_kennedy wrote:

Normally helpers are in a Helpers folder alongside the MacOS folder.

That’s one option, but storing helpers in Contents/MacOS is also allowed. For more about this, see Placing Content in a Bundle.

Share and Enjoy

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


To find the main executable of a helper app:

  1. Call the url(forAuxiliaryExecutable:) method on the main bundle to get a url for helper app.

  2. Construct a bundle from that URL.

  3. Use the executableURL property to get the helper app’s executable.

I will go ahead and start reading about XPC.

So that’s tricky as well, because XPC does not have good support for parent/child IPC )-:

Can you explain a bit more about your IPC needs? In your first post you had this:

<string>com.foo.parent.is_first_run</string>
<string>com.foo.parent.activation_complete</string>
<string>com.foo.parent.sign_out</string>
<string>com.foo.parent.get_version</string>

which gives me some idea as to the scope of this problem. Can you expand on that?

Share and Enjoy

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

The parent app is a persistent app that presents an icon and menu in the toolbar

I’m like the clarify that last bit. What toolbar are you talking about here? The right side of the main menu bar? Using APIs like NSStatusItem and NSStatusBar?

Share and Enjoy

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

How to enable AppleScript between Parent and Child applications within sandbox for Mac App Store?
 
 
Q