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

如何从Safari工具栏项扩展获取内容拦截器扩展的启用状态?

问题分析与解决方案

首先,直接在Safari工具栏项扩展中调用SFContentBlockerManager.getStateOfContentBlocker获取内容拦截器状态总是返回false,核心原因是Safari的扩展沙箱隔离机制:工具栏项扩展和内容拦截器扩展是两个独立的沙箱进程,默认没有权限跨扩展访问其他扩展的状态信息,所以这个调用无法拿到真实状态。

支持跨扩展直接获取状态吗?

目前Safari扩展框架不支持直接跨扩展获取其他扩展的状态,沙箱安全限制阻断了这种直接访问。不过我们可以通过主应用(Containing App)作为中间层来实现状态共享,这是官方推荐的合规方案。

具体实现方案

方案1:通过App Group共享状态(最简单高效)

利用App Group让主应用、工具栏项扩展、内容拦截器扩展共享一份存储,主应用负责获取并同步内容拦截器的状态,工具栏扩展从共享存储读取状态。

步骤如下:

  1. 配置App Group:
    在Xcode中,给主应用和两个扩展都开启App Groups能力,添加同一个Group ID(比如group.com.xyz.macapp),确保三者都加入同一个App Group。

  2. 主应用同步状态:
    主应用负责获取内容拦截器的真实状态,并写入共享的UserDefaults,同时监听状态变化实时更新:

    // 主应用中的代码
    func setupContentBlockerStateSync() {
        // 初始同步状态
        updateContentBlockerState()
        
        // 监听状态变化通知,实时更新
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(updateContentBlockerState),
            name: SFContentBlockerManager.stateDidChangeNotification,
            object: nil
        )
    }
    
    @objc private func updateContentBlockerState() {
        SFContentBlockerManager.getStateOfContentBlocker(withIdentifier: "com.xyz.macapp.contentblocker") { state, error in
            guard let state = state else {
                // 处理错误
                return
            }
            let sharedDefaults = UserDefaults(suiteName: "group.com.xyz.macapp")
            sharedDefaults?.set(state.isEnabled, forKey: "ContentBlockerEnabled")
            sharedDefaults?.synchronize()
        }
    }
    

    记得在主应用启动时调用setupContentBlockerStateSync()。

  3. 工具栏扩展读取状态:
    修改你的onPopoverVisible方法,从共享UserDefaults读取状态:

    func onPopoverVisible() {
        let sharedDefaults = UserDefaults(suiteName: "group.com.xyz.macapp")
        // 默认值设为false,避免读取失败时出错
        let isContentBlockerEnabled = sharedDefaults?.bool(forKey: "ContentBlockerEnabled") ?? false
        
        if isContentBlockerEnabled {
            // 显示工具栏项内容,隐藏警告
        } else {
            // 显示警告提示,隐藏工具栏功能内容
        }
    }
    

方案2:通过XPC通信(更灵活,适合复杂交互)

如果你的应用需要更复杂的跨进程通信(比如除了状态还要传递其他数据),可以用XPC服务:

  • 主应用作为XPC服务的提供者,暴露一个获取内容拦截器状态的接口。
  • 工具栏项扩展作为XPC客户端,向主应用发送请求,主应用处理后返回真实状态。
    这种方式比App Group更安全,适合敏感数据,但实现起来稍复杂一些。

替代方案:内容拦截器主动同步状态

当内容拦截器被启用时,它可以主动将自身状态写入共享的App Group存储,但要注意:内容拦截器只有在启用状态下才会运行,如果用户禁用了它,它无法主动更新状态,所以这个方案必须和主应用的状态同步结合使用,作为补充。

比如在内容拦截器的SFSafariExtensionHandler的init方法中写入状态:

class ContentBlockerExtensionHandler: SFSafariExtensionHandler {
    override init() {
        super.init()
        let sharedDefaults = UserDefaults(suiteName: "group.com.xyz.macapp")
        sharedDefaults?.set(true, forKey: "ContentBlockerEnabled")
        sharedDefaults?.synchronize()
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:41:40