You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让AppKit macOS应用无阻塞检查AppleScript控制其他应用权限

无阻塞检查Apple事件自动化权限并触发授权弹窗

1. 无阻塞权限检查

你可以通过Security框架的SMCopyAuthorizationRights接口直接查询当前应用是否拥有对目标App的Automation权限,这个操作耗时极短,也可以放到后台队列执行完全不影响主线程。

Objective-C示例:

#import <Security/Security.h>

BOOL hasAutomationPermissionForTargetApp(NSString *targetBundleID) {
    AuthorizationRef authRef;
    OSStatus status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authRef);
    if (status != errAuthorizationSuccess) {
        AuthorizationFree(authRef, kAuthorizationFlagDefaults);
        return NO;
    }

    AuthorizationItem authItem = {
        .name = "com.apple.security.automation.apple-events",
        .valueLength = targetBundleID.length,
        .value = (void *)targetBundleID.UTF8String,
        .flags = 0
    };
    AuthorizationRights authRights = {1, &authItem};

    AuthorizationFlags flags = kAuthorizationFlagExtendRights | kAuthorizationFlagPreAuthorize | kAuthorizationFlagInteractionAllowed;
    status = AuthorizationCopyRights(authRef, &authRights, NULL, flags, NULL);
    
    BOOL hasPermission = (status == errAuthorizationSuccess);
    AuthorizationFree(authRef, kAuthorizationFlagDefaults);
    return hasPermission;
}

把检查逻辑放到后台队列,避免阻塞主线程:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    BOOL hasPermission = hasAutomationPermissionForTargetApp(@"com.apple.Safari"); // 替换为目标App的Bundle ID
    dispatch_async(dispatch_get_main_queue(), ^{
        // 回到主线程处理结果
        if (!hasPermission) {
            triggerAutomationAuthorizationForTargetApp(@"com.apple.Safari");
        }
    });
});

2. 异步触发授权弹窗

如果检测到无权限,你可以在后台队列执行一段极简的AppleScript(比如仅获取目标App名称),这样即使系统弹出授权对话框,阻塞的也只是后台线程,完全不影响主应用的UI交互。

Objective-C示例:

void triggerAutomationAuthorizationForTargetApp(NSString *targetBundleID) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSString *scriptSource = [NSString stringWithFormat:@"tell application id \"%@\" to name", targetBundleID];
        NSAppleScript *script = [[NSAppleScript alloc] initWithSource:scriptSource];
        NSDictionary *errorInfo = nil;
        [script executeAndReturnError:&errorInfo];
        // 无需处理执行结果,只要触发授权弹窗即可
    });
}

3. Swift版本参考

import Security

func hasAutomationPermission(for targetBundleID: String) -> Bool {
    var authRef: AuthorizationRef?
    let status = AuthorizationCreate(nil, nil, [], &authRef)
    guard status == errAuthorizationSuccess, let auth = authRef else {
        return false
    }
    defer { AuthorizationFree(auth, []) }
    
    let authItem = AuthorizationItem(
        name: "com.apple.security.automation.apple-events",
        valueLength: targetBundleID.utf8.count,
        value: UnsafeMutableRawPointer(mutating: targetBundleID.utf8CString),
        flags: 0
    )
    var authRights = AuthorizationRights(count: 1, items: &authItem)
    
    let flags: AuthorizationFlags = [.extendRights, .preAuthorize, .interactionAllowed]
    let authStatus = AuthorizationCopyRights(auth, &authRights, nil, flags, nil)
    
    return authStatus == errAuthorizationSuccess
}

// 异步检查并触发授权
DispatchQueue.global().async {
    let hasPermission = self.hasAutomationPermission(for: "com.apple.Safari")
    DispatchQueue.main.async {
        if !hasPermission {
            self.triggerAuthorization(for: "com.apple.Safari")
        }
    }
}

func triggerAuthorization(for targetBundleID: String) {
    DispatchQueue.global().async {
        let scriptSource = "tell application id \"\(targetBundleID)\" to name"
        let script = NSAppleScript(source: scriptSource)
        var error: NSDictionary?
        script?.executeAndReturnError(&error)
    }
}

关键注意事项

  • 必须使用目标App的Bundle ID而非显示名称,避免同名App导致的识别错误。
  • 后台执行AppleScript时,授权弹窗的阻塞范围仅限于后台线程,主应用的交互完全不受影响。
  • 如果用户在系统设置中手动修改了权限,建议延迟几秒后重新检查权限状态,保证结果准确性。

内容的提问来源于stack exchange,提问作者Bri Bri

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 04:42:14