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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 05:30:20