在 MAS 软件内调用 Apple Script

 • 

最近写了一个小软件 WebDrop ,尝试了下上架,关于如何在沙盒内调用 Apple Script 写一点相关的内容。
先看一个 Code Snippet

NSAppleScript *script= [[NSAppleScript alloc] initWithSource:@"tell application \"Safari\" to return URL of front document as string"];  
    NSDictionary *scriptError = nil;
    NSAppleEventDescriptor *descriptor = [script executeAndReturnError:&scriptError];
    if(scriptError)
    {
        NSLog(@"Error: %@",scriptError);
    } else
    {
        NSAppleEventDescriptor *unicode = [descriptor coerceToDescriptorType:typeUnicodeText];
        NSData *data = [unicode data];
        NSString *result = [[NSString alloc] initWithCharacters:(unichar*)[data bytes] length:[data length] / sizeof(unichar)];
    }

这段代码会返回当前 Safari 最前 Tab 的网页地址,但是只限于没有开启 App Sandbox 的情况 —— 如果开启了 Sandbox,就会出现以下错误:

2016-06-24 10:41:10.022 WebDrop[81536:1628248] Error: {  
    NSAppleScriptErrorAppName = Safari;
    NSAppleScriptErrorBriefMessage = "\U5e94\U7528\U7a0b\U5e8f\U6ca1\U6709\U8fd0\U884c\U3002";
    NSAppleScriptErrorMessage = "\U201cSafari\U201d\U9047\U5230\U4e00\U4e2a\U9519\U8bef\Uff1a\U5e94\U7528\U7a0b\U5e8f\U6ca1\U6709\U8fd0\U884c\U3002";
    NSAppleScriptErrorNumber = "-600";
    NSAppleScriptErrorRange = "NSRange: {61, 6}";
}

其中的 Unicode 为:“应用程序没有运行。”,这是因为沙盒阻止 Apple Script 访问 Safari 了,这个时候有两个解决办法。

第一个见 http://objccn.io/issue-14-2/,首先这个方法好像是不能上 MAS 的,因为其中采用的方法是安装 .scpt 到用户文件夹下,我用这个方法上架时审核人员给出以下的理由:

Performance - 2.4.5  
Your app installs an AppleScript in shared locations.  
Specifically the app installs an AppleScript into the Application Scripts folder.  

我也不知道为什么审核人员不给用这种方法,而必须要求 Apple Script 储存在 Container 内 (参见 stackoverflow),不过既然如此,就只能用另一个方法了...

第二个就是加 Entitlement key,有两个 key 可以添加,一个是 com.apple.security.scripting-targets (Reference),一个是 com.apple.security.temporary-exception.apple-events (Reference),其中前者更推荐,使用起来是这样的:

<key>com.apple.security.scripting-targets</key>  
    <dict>
        <key>com.apple.mail</key>
        <array>
            <string>com.apple.mail.compose</string>
        </array>
    </dict>

后者的话简单一些,不用配置特定的动作,只要写 bundle id 就可以(注意要全小写)

<key>com.apple.security.temporary-exception.apple-events</key>  
    <array>
        <string>com.google.chrome.canary</string>
        <string>com.google.chrome</string>
        <string>com.apple.safari</string>
        <string>com.vivaldi.vivaldi</string>
        <string>org.webkit.nightly.webKit</string>
    </array>