swiftmacostagsspotlightnsmetadataquery

Not able to get results for spotlight search for user tags with NSMetadataquery and kMDItemUserTags


I am at my wits end after trying to add a routine to my controller to search the user's computer for user files with specified user (Mavericks+) tags. I am running into a block where the search will start, but no results are returned and the operation queue is nil.

Here is the code:

class myController: NSWindowController {

    //define metadataQuery 
    var metadataQuery: NSMetadataQuery!
    var metadataQueryDidUpdateObserver: AnyObject?
    var metadataQueryDidFinishGatheringObserver: AnyObject?

    //initialize notificationsCenter
    let notificationsCenter = NotificationCenter.default

func doSpotlightSearch(){

    //add observers
    notificationsCenter.addObserver(self, selector: "initalGatherComplete:", name: NSNotification.Name.NSMetadataQueryDidFinishGathering, object: nil)
    notificationsCenter.addObserver(self, selector: "batchReturned:", name: NSNotification.Name.NSMetadataQueryDidUpdate, object: nil)


    metadataQuery = NSMetadataQuery()
    metadataQuery.searchScopes = [NSMetadataQueryIndexedLocalComputerScope]
    metadataQuery.predicate = NSPredicate(format: "kMDItemUserTags == 'myTags.testTag'")

    //start search
    metadataQuery.start()

    //test to see if query is up and running
    while metadataQuery.isGathering{
        print(metadataQuery.operationQueue?.operationCount)
        print ("result count: \(metadataQuery.resultCount)")
        sleep(1)
    }

func batchReturned(_ sender: NSNotification) {
    print("Running batchReturned func")

    let resultCounter = metadataQuery.resultCount
    print("Number of results:\(resultCounter)")
    let notificationsCenter = NotificationCenter.default

    notificationsCenter.removeObserver(self, name: NSNotification.Name.NSMetadataQueryDidFinishGathering, object: nil)
}

func initalGatherComplete(_ sender: NSNotification) {
    print("Running initialGatherComplete func")

    metadataQuery.stop()
    let resultCounter = metadataQuery.resultCount
    print("Number of results:\(resultCounter)")
    let notificationsCenter = NotificationCenter.default
    notificationsCenter.removeObserver(self, name: NSNotification.Name.NSMetadataQueryDidFinishGathering, object: nil)
}

When I run the above code, it shows the results queue as being nil and the result count as zero. Even if I take out the isGathering loop, the batchReturned and initialGatherComplete functions never get called.

I suspect that there may be an issue with the predicate formatting, but this works fine when I put the query into the terminal with mdfind.


Solution

  • From documentation of NSMetadataQuery.start() method: This method must be called from the receiver’s operationQueue or on the main thread.

    If you are not calling start() method from main thread you are responsible to create OperationQueue yourself and start if from there, e.g:

    // Declare on class-level
    private lazy var queryOperationQueue: OperationQueue = {
      let queue = OperationQueue()
      queue.maxConcurrentOperationCount = 1 // restriction for NSMetadataQuery
      return queue
    }()
    
    
    ...
    
    func doSpotlightSearch() {
      metadataQuery = NSMetadataQuery()
      metadataQuery.searchScopes = [NSMetadataQueryIndexedLocalComputerScope]
      metadataQuery.predicate = NSPredicate(format: "kMDItemUserTags == 'myTags.testTag'")
    
      // assign queue to NSMetadataQuery
      metadataQuery.operationQueue = queryOperationQueue
    
      // run query from its queue
      metadataQuery.operationQueue?.addOperation {
        metadataQuery.start()
      }
    }
    

    Hope it's clear.