5

我目前正在构建一个 mac 应用程序,将来应该能够在 OS X 上杀死和启动应用程序。

为此,我需要找到一种方法来获取机器上所有已安装应用程序的列表。

我已经做了很多研究,并决定使用 Spotlight 和 NSMetadataQuery 来获取列表。

我能够找到关于上述主题的这篇文章,并开始在 Swift 2.2(该项目的首选武器)中实现该功能。通过一些翻译,我能够使其工作,代码现在成功构建并运行。但是,在运行时,我似乎遇到了查询本身的问题:

<NSMetadataQuery: 0x6080000e3880> is being deallocated without first calling -stopQuery. To avoid race conditions, you should first invoke -stopQuery on the run loop on which -startQuery was called

这是我目前正在使用的代码。

    public func doSpotlightQuery() {
    query = NSMetadataQuery()
    let predicate = NSPredicate(format: "kMDItemKind ==[c] %@", "Application")
    let defaultNotificationCenter = NSNotificationCenter()
    defaultNotificationCenter.addObserver(self, selector: #selector(queryDidFinish(_:)), name: NSMetadataQueryDidFinishGatheringNotification, object: nil)
    query.predicate = predicate
    query.startQuery()
}

public func queryDidFinish(notification: NSNotification) {
    for i in 0 ... query.resultCount {
        print(query.resultAtIndex(i).valueForAttribute(kMDItemDisplayName as String))
    }
}

测试

mdfind "kMDItemKind == 'Application'"

我的mac终端中的命令(各种变体)也没有给我任何结果,这让我想到了我的问题:

我是否以错误的方式设置了查询,或者此命令在“El Capitan”中不起作用?

有人可以帮我找出我的错误吗?我肯定很想最终完成这项工作!

4

2 回答 2

2

dealloc 消息似乎查询缺少强引用。

var query: NSMetadataQuery? {
    willSet {
        if let query = self.query {
            query.stopQuery()
        }
    }
}

public func doSpotlightQuery() {
    query = NSMetadataQuery()
    let predicate = NSPredicate(format: "kMDItemKind ==[c] %@", "Application")
    let defaultNotificationCenter = NSNotificationCenter()
    defaultNotificationCenter.addObserver(self, selector: #selector(queryDidFinish(_:)), name: NSMetadataQueryDidFinishGatheringNotification, object: nil)
    query?.predicate = predicate
    query?.startQuery()
}

public func queryDidFinish(notification: NSNotification) {
    guard let query = notification.object as? NSMetadataQuery else {
        return
    }

    for i in 0 ... query.resultCount {
        print(query.resultAtIndex(i).valueForAttribute(kMDItemDisplayName as String))
    }
}

我建议使用不同的谓词,因为 kMDItemKind 是根据约翰在这里的评论的本地化键

所以let predicate = NSPredicate(format: "kMDItemContentType == 'com.apple.application-bundle'")会为我们正在做的事情工作。

在 swift 3 中,这可能如下所示:

var query: NSMetadataQuery? {
    willSet {
        if let query = self.query {
            query.stop()
        }
    }
}

public func doSpotlightQuery() {
    query = NSMetadataQuery()
    let predicate = NSPredicate(format: "kMDItemContentType == 'com.apple.application-bundle'")
    NotificationCenter.default.addObserver(self, selector: #selector(queryDidFinish(_:)), name: NSNotification.Name.NSMetadataQueryDidFinishGathering, object: nil)
    query?.predicate = predicate
    query?.start()
}

public func queryDidFinish(_ notification: NSNotification) {
    guard let query = notification.object as? NSMetadataQuery else {
        return
    }

    for result in query.results {
        guard let item = result as? NSMetadataItem else {
            print("Result was not an NSMetadataItem, \(result)")
            continue
        }
        print(item.value(forAttribute: kMDItemDisplayName as String))
    }
}
于 2017-02-09T16:58:44.783 回答
1

这是一个获取 /applications 和 /applications/utilities 的内容并将内容转换为NSMetaDataItems.

public func getAllApplications() -> [NSMetadataItem] {
    let fileManager = FileManager()

    guard let applicationsFolderUrl = try? FileManager.default.url(for: .applicationDirectory, in: .localDomainMask, appropriateFor: nil, create: false) else { return [] }

    let applicationUrls = try! fileManager.contentsOfDirectory(at: applicationsFolderUrl , includingPropertiesForKeys: [], options: [FileManager.DirectoryEnumerationOptions.skipsPackageDescendants, FileManager.DirectoryEnumerationOptions.skipsSubdirectoryDescendants])

    guard let systemApplicationsFolderUrl = try? FileManager.default.url(for: .applicationDirectory, in: .systemDomainMask, appropriateFor: nil, create: false) else { return [] }

    let utilitiesFolderUrl = NSURL.init(string: "\(systemApplicationsFolderUrl.path)/Utilities") as! URL

    guard let utilitiesUrls = try? fileManager.contentsOfDirectory(at: utilitiesFolderUrl, includingPropertiesForKeys: [], options: [FileManager.DirectoryEnumerationOptions.skipsPackageDescendants, FileManager.DirectoryEnumerationOptions.skipsSubdirectoryDescendants]) else { return [] }

    let urls = applicationUrls + utilitiesUrls

    var applications = [NSMetadataItem]()

    for url in urls {
        print(url.path, fileManager.isExecutableFile(atPath: url.path))
        if fileManager.isExecutableFile(atPath: url.path) {
            guard let mdi = NSMetadataItem(url: url) else { continue }
            applications.append(mdi)
        }
    }

    for app in applications {
        print(app.value(forAttribute: kMDItemDisplayName as String))
    }
    return applications
}

(可以对其进行一些清理,但我很匆忙地写了它)

于 2020-04-14T09:28:55.360 回答