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

macOS File Provider Extension:特定目录刷新失败(仅.workingSet有效)

macOS File Provider扩展:signalEnumerator仅对.workingSet生效的原因与解决方法

原因分析

  1. 枚举器活跃性限制:signalEnumerator(for:)仅能向当前处于活跃状态的枚举器发送信号。.workingSet对应的枚举器是系统默认持续保持活跃的(它对应用户近期访问的文件集合),而其他目录的枚举器在完成一次枚举任务后会被系统自动释放,因此无法接收信号。
  2. 枚举器未实现信号响应:如果自定义的FileProviderEnumerator没有重写signal()方法,即使枚举器处于活跃状态,也无法处理刷新信号。

解决方法

1. 先激活目标目录的枚举器

在调用signalEnumerator前,主动触发一次目标目录的枚举请求,让系统创建并保留枚举器实例:

func performAction(identifier actionIdentifier: NSFileProviderExtensionActionIdentifier, onItemsWithIdentifiers itemIdentifiers: [NSFileProviderItemIdentifier], completionHandler: @escaping (Error?) -> Void) -> Progress {
    switch actionIdentifier.rawValue {
    case "Refresh":
        guard let targetID = itemIdentifiers.first else {
            completionHandler(NSError(domain: NSCocoaErrorDomain, code: NSInvalidArgumentError, userInfo: [NSLocalizedDescriptionKey: "No item identifier provided"]))
            return Progress()
        }
        
        // 先激活目标目录的枚举器
        do {
            let enumerator = try self.enumerator(for: targetID, request: NSFileProviderRequest())
            // 发起空枚举请求,让系统保留枚举器
            enumerator.enumerateItems(for: NSFileProviderEnumerationRequest(pageSize: 0), startingAt: nil) { _, _, error in
                if let error = error {
                    Logger.log.info("[ENUM] Failed to activate enumerator: \(String(describing: error), privacy: .public)")
                }
            }
        } catch {
            Logger.log.info("[ENUM] Failed to get enumerator: \(String(describing: error), privacy: .public)")
        }
        
        // 发送刷新信号
        manager.signalEnumerator(for: targetID) { error in
            if let error = error {
                Logger.log.info("[SIGNAL] Failed to signal enumerator: \(String(describing: error), privacy: .public)")
                completionHandler(error)
            } else {
                Logger.log.info("[SIGNAL] Signalled Successfully: \(String(describing: targetID), privacy: .public)")
                completionHandler(nil)
            }
        }
    default:
        completionHandler(NSError(domain: NSCocoaErrorDomain,
                                  code: NSFeatureUnsupportedError,
                                  userInfo: [NSLocalizedDescriptionKey: "The action is not supported"]))
    }
    return Progress()
}

2. 完善自定义枚举器的信号响应

在FileProviderEnumerator中重写signal()方法,收到信号后重新枚举目录内容并通知系统更新:

class FileProviderEnumerator: NSFileProviderEnumerator {
    private let enumeratedItemIdentifier: NSFileProviderItemIdentifier
    
    init(enumeratedItemIdentifier: NSFileProviderItemIdentifier) {
        self.enumeratedItemIdentifier = enumeratedItemIdentifier
        super.init()
    }
    
    override func invalidate() {
        // 清理资源
        super.invalidate()
    }
    
    override func signal() {
        // 收到刷新信号后,重新枚举目录内容
        self.enumerateItems(for: NSFileProviderEnumerationRequest(pageSize: 50), startingAt: nil) { items, nextPage, error in
            guard error == nil else {
                Logger.log.info("[ENUM] Failed to enumerate items: \(String(describing: error!), privacy: .public)")
                return
            }
            // 通知系统目录内容已更新
            NSFileProviderManager.default.signalContentsChanged(for: self.enumeratedItemIdentifier)
        }
    }
    
    override func enumerateItems(for request: NSFileProviderEnumerationRequest, startingAt page: NSFileProviderPage?, completionHandler: @escaping ([NSFileProviderItem]?, NSFileProviderPage?, Error?) -> Void) {
        // 实现从远程服务器获取目录内容的逻辑
        // ...
        completionHandler(fetchedItems, nil, nil)
    }
}

3. 辅助方案:结合reloadItem强制刷新

如果signalEnumerator仍不生效,可以先调用reloadItem标记目录需要更新,再触发信号:

// 在signalEnumerator前添加
NSFileProviderManager.default.reloadItem(at: targetID) { error in
    if let error = error {
        Logger.log.info("[RELOAD] Failed to reload item: \(String(describing: error), privacy: .public)")
    }
}

注意事项

  • 确保目标目录的NSFileProviderItem完整实现了itemIdentifier、parentItemIdentifier等核心属性,系统依赖这些信息定位枚举器。
  • 避免频繁激活枚举器,系统对活跃枚举器的数量有上限,超出后会自动回收旧实例。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 01:27:48