我目前正在构建一个mac应用程序,将来应该能够在OS X上杀死并启动应用程序。
为了实现这一点,我需要找到一种方法来获取机器上所有已安装应用程序的列表。
我已经做了很多研究,并决定使用Spotlight和NSMetadataQuery来获取列表。
我能够找到关于所提到主题的this post并开始在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&#39;
中不起作用?有人可以帮我找到我的错误吗?我一定很想最终完成这项工作!
答案 0 :(得分:0)
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))
}
}
我建议使用不同的谓词,因为根据John的评论here
,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))
}
}
答案 1 :(得分:0)
这是一个获取/ 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
}
(可以做一些清理工作,但我急着写了)