如何在Swift中实现终端式Spotlight搜索?NSMetadataQuery可用吗?
在Swift中实现一周内运行过的应用查询
方法一:适配NSMetadataQuery的谓词写法
mdfind的语法和NSPredicate存在差异,不能直接将mdfind查询字符串传入NSMetadataQuery,需要手动转换语法:
- 先计算对应时间范围的
Date对象,替换mdfind中的$time.today变量 - 按照Core Data谓词语法重构查询条件
- 配置NSMetadataQuery的搜索范围并监听查询完成事件
示例代码:
import Foundation func findRecentlyUsedApps() { let calendar = Calendar.current guard let sevenDaysAgo = calendar.date(byAdding: .day, value: -7, to: Date()), let tomorrow = calendar.date(byAdding: .day, value: 1, to: Date()) else { print("日期计算失败") return } // 对应原mdfind查询逻辑的谓词 let predicate = NSPredicate(format: """ (kMDItemFSContentChangeDate >= %@ AND kMDItemFSContentChangeDate <= %@) AND kMDItemContentTypeTree == 'com.apple.application' AND (kMDItemLastUsedDate >= %@ AND kMDItemLastUsedDate <= %@) """, sevenDaysAgo as CVarArg, tomorrow as CVarArg, sevenDaysAgo as CVarArg, tomorrow as CVarArg) let query = NSMetadataQuery() query.predicate = predicate query.searchScopes = [NSMetadataQueryUbiquitousDataScope, NSMetadataQueryLocalComputerScope] // 监听查询完成通知 NotificationCenter.default.addObserver(forName: .NSMetadataQueryDidFinishGathering, object: query, queue: .main) { notification in guard let query = notification.object as? NSMetadataQuery else { return } query.disableUpdates() NotificationCenter.default.removeObserver(self, name: .NSMetadataQueryDidFinishGathering, object: query) // 提取应用路径 for result in query.results { guard let item = result as? NSMetadataItem, let url = item.value(forAttribute: kMDItemPath as String) as? String else { continue } print("找到应用:\(url)") } } query.start() } // 执行查询 findRecentlyUsedApps()
关键注意事项:
- 谓词中使用
==而非mdfind的=,符合NSPredicate语法规范 - NSMetadataQuery为异步执行,必须通过通知获取结果
- 搜索范围需指定为本地设备或全局,匹配mdfind默认行为
方法二:直接调用mdfind命令行工具
如果不想适配谓词语法,可以直接在Swift中调用终端的mdfind命令,复用原查询语句:
示例代码:
import Foundation func runMdfindQuery() { let mdfindCmd = """ mdfind '(InRange(kMDItemFSContentChangeDate,$time.today(-7d),$time.today(+1d)) && ((kMDItemContentTypeTree=com.apple.application) && InRange(kMDItemLastUsedDate,$time.today(-7d),$time.today(+1d))))' """ let task = Process() task.launchPath = "/bin/bash" task.arguments = ["-c", mdfindCmd] let pipe = Pipe() task.standardOutput = pipe do { try task.run() let outputData = pipe.fileHandleForReading.readDataToEndOfFile() if let outputStr = String(data: outputData, encoding: .utf8) { let appPaths = outputStr.components(separatedBy: .newlines).filter { !$0.isEmpty } appPaths.forEach { print("找到应用:\($0)") } } task.waitUntilExit() } catch { print("命令执行失败:\(error.localizedDescription)") } } // 执行查询 runMdfindQuery()
关键注意事项:
- 无需修改原mdfind查询语句,直接复用即可
- 需要处理命令执行的错误情况,以及输出结果的分割解析
- 权限与终端执行mdfind一致,无需额外配置
内容的提问来源于stack exchange,提问作者Andrew
相关产品推荐
相关产品推荐

