Swift/Cocoa实现macOS文件夹监听:仅监听文件增删(不含子文件夹)
macOS 文件夹变更监听(仅直接子项)实现方案
嘿,这个需求用系统原生的NSFilePresenter配合NSFileCoordinator完全能搞定,不用依赖任何第三方框架,我给你一步步拆解具体实现:
核心思路
NSFilePresenter是Apple提供的用于监听文件/文件夹变更的原生API,它能和NSFileCoordinator协同工作,精准捕获文件系统的各类变更事件。我们只需要实现该协议的关键方法,再过滤掉子文件夹内的变更,就能只监听目标文件夹的直接子项添加/移除事件。
完整代码实现
首先创建一个监听类,遵循NSFilePresenter协议:
import Foundation class FolderMonitor: NSObject, NSFilePresenter { // 要监听的目标文件夹URL private let targetFolderURL: URL // 处理变更通知的操作队列(可根据需求指定主队列/后台队列) private let processingQueue: OperationQueue init(folderURL: URL) { self.targetFolderURL = folderURL self.processingQueue = OperationQueue() super.init() // 注册当前实例为文件监听者 NSFileCoordinator.addFilePresenter(self) } deinit { // 销毁时移除监听,避免内存泄漏 NSFileCoordinator.removeFilePresenter(self) } // MARK: - NSFilePresenter 协议必填方法 var presentedItemURL: URL? { return targetFolderURL } var presentedItemOperationQueue: OperationQueue { return processingQueue } // MARK: - 监听子项添加事件 func presentedSubitemDidAppear(at url: URL) { // 只处理目标文件夹的直接子项,过滤子文件夹内的变更 guard url.deletingLastPathComponent() == targetFolderURL else { return } // 区分是文件还是文件夹(可选) var isDirectory: ObjCBool = false if FileManager.default.fileExists(atPath: url.path, isDirectory: &isDirectory) { let itemType = isDirectory.boolValue ? "文件夹" : "文件" print("\(itemType)已添加: \(url.lastPathComponent)") } // 在这里添加你的业务逻辑,比如通知UI更新等 // 如果需要更新UI,记得切换到主队列: // DispatchQueue.main.async { /* 更新UI代码 */ } } // MARK: - 监听子项移除事件 func presentedSubitemDidDisappear(at url: URL) { guard url.deletingLastPathComponent() == targetFolderURL else { return } var isDirectory: ObjCBool = false if FileManager.default.fileExists(atPath: url.path, isDirectory: &isDirectory) { let itemType = isDirectory.boolValue ? "文件夹" : "文件" print("\(itemType)已移除: \(url.lastPathComponent)") } // 处理移除后的业务逻辑 } // 可选:监听文件夹自身属性变更(比如重命名、权限修改等) func presentedItemDidChange() { print("目标文件夹属性已变更") } }
使用示例
在需要监听的地方初始化这个类,注意必须保持实例的强引用,否则监听会自动停止:
// 示例:监听用户文档目录 if let documentsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first { let folderMonitor = FolderMonitor(folderURL: documentsDir) // 务必将folderMonitor存储在全局变量/类属性中,避免被销毁 }
关键注意点
- 强引用监听实例:如果
FolderMonitor实例被释放(比如作用域结束),deinit会触发并移除监听,导致后续无法接收变更通知。一定要把它存在全局变量或者类的属性里。 - 过滤子文件夹变更:通过判断变更项的父URL是否等于目标文件夹URL,确保我们只处理直接子项的变化,忽略子文件夹内部的操作。
- 操作队列选择:
processingQueue可以指定为OperationQueue.main(如果需要直接更新UI),或者后台队列(处理耗时逻辑),根据你的业务需求调整。 - 系统兼容性:这个方案适用于所有支持
NSFilePresenter的macOS版本(macOS 10.7+),完全原生,不需要额外依赖。
内容的提问来源于stack exchange,提问作者eivindml
相关产品推荐
相关产品推荐

