Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

List installed Applications on El Capitan using Spotlight in Swift 2.2

I am currently building a mac app which in the future should be able to kill and start apps on OS X.

For that to be possible, I need to find a way to get a list of all the installed applications on the machine.

I already did quite a bit of research and have decided to use Spotlight with NSMetadataQuery to be able to get the list.

I was able to find this post about the mentioned topic and started to implement the functionality in Swift 2.2 (the weapon of choice for the project). With a bit of translation I was able to make it work and the code now successfully builds and runs. During runtime, however, I seem to be having a problem with the query itself:

<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

This is the code I am currently using.

    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))
    }
}

Testing the

mdfind "kMDItemKind == 'Application'"

command (with variations of all kind) in the terminal of my mac didn't give me any results either which brings me to my question:

Did I set up the query in a wrong way or does this command not work in 'El Capitan'?

Can someone please help me find my mistake? I'd sure love to finally make this work!

like image 266
bob_mosh Avatar asked Oct 18 '22 06:10

bob_mosh


1 Answers

The dealloc message seems like the query is missing a strong reference.

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))
    }
}

I'd suggest using a different predicate as kMDItemKind is a localized key according to John's comment here

so let predicate = NSPredicate(format: "kMDItemContentType == 'com.apple.application-bundle'") would work for what we're doing.

In swift 3 this could look like this:

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))
    }
}
like image 92
Maximillian Rose Avatar answered Oct 21 '22 04:10

Maximillian Rose