2

By default NSMetaDataQuery results notifications are received on the main thread. It seems you have to call query.startQuery on the main thread, but you can use [query.setOperationQueue:] to set the queue to which results notifications will be sent.

Ideally I want a background thread to be receiving these results and processing them and depending on what files are found, I will set variables or post notifications to the main thread.

What I don't understand is how I create the operational queue and how I process the notification on this queue. Sample code for whatever classes are required and details of where and how they should be created would be great. I have read the Apple docs but can't get my head around how you create a queue that can process notifications.

Thanks

benams
  • 4,308
  • 9
  • 32
  • 74
Duncan Groenewald
  • 8,496
  • 6
  • 41
  • 76

2 Answers2

6

You can try to use this code to process notifications in background thread:

   NSMetadataQuery *query = [NSMetadataQuery new];
   [query enableUpdates];
   // Subscribe to query updates and process then in background thread
   [[NSNotificationCenter defaultCenter] addObserverForName:
    NSMetadataQueryDidUpdateNotification
   object:nil queue:[NSOperationQueue new]
   usingBlock:^(NSNotification __strong *notification)
   {
      // disable the query while iterating
    [query disableUpdates];
     for (NSMetadataItem *item in query.results)
     {
         //do here everything you want with the results 
        //e.g. get values with [item valueForAttribute:NSMetadataItemFSNameKey];
         }          
       [query enableUpdates];
        }];
    [query startQuery];

Just for the tip:

 [NSOperationQueue mainQueue] // added operations execute on the main thread
 [NSOperationQueue new] // iOS 4 and higher - guaranteed to be on the background thread

P.S to process first results you should subscribe to NSMetadataQueryDidFinishGatheringNotification - it will be posted when the receiver has finished with the initial result-gathering phase of the query and for all subsequent updates you should subscribe to NSMetadataQueryDidUpdateNotification as in my example because as you know queries have two phases: the initial gathering phase that collects all currently matching results and a second live-update phase.

Oleksandr Karaberov
  • 12,573
  • 10
  • 43
  • 70
  • Thanks, that looks great, so simple! I'll give it a go in a couple of hours just to confirm it works. – Duncan Groenewald Mar 01 '14 at 10:49
  • 1
    Minor point with the above answer: If you register block based observers like this, you should store the return value to "addObserverForName" and manually remove it later, otherwise you'll leak the observer.From the Apple doc for this method: "The notification center copies the block. The notification center strongly holds the copied block until you remove the observer registration.". Unlike selector based observers, these don't get cleaned up automatically for you. – rustyshelf Feb 02 '23 at 23:45
1

This was very helpful. This is the code I used to implement this in Swift if anyone is looking for this:

let nf = NotificationCenter.default

    //add background queue observer for updates during live phase
    nf.addObserver(forName: NSNotification.Name.NSMetadataQueryDidUpdate, object: nil, queue: self.backGroundQueue, using:{_ in 
        self.query.disableUpdates()
        self.resultsArray = self.query.results as! [ResultItem]
        self.query.enableUpdates()
    })

    //add background queue observer for updates during gathering
    nf.addObserver(forName: NSNotification.Name.NSMetadataQueryGatheringProgress, object: nil, queue: self.backGroundQueue, using:{_ in
        self.query.disableUpdates()
        self.resultsArray = self.query.results as! [ResultItem]
        print("progressing... ")
        print("number of results: \(self.query.results.count)")
        self.query.enableUpdates()
    })

    //add background queue observer for finishing gathering phase
    nf.addObserver(forName: NSNotification.Name.NSMetadataQueryDidFinishGathering, object: nil, queue: self.backGroundQueue, using:{_ in
        self.query.disableUpdates()
        self.resultsArray = self.query.results as! [ResultItem]
        self.query.enableUpdates()
    })


    self.query.searchScopes = [self.thePath]
    self.query.predicate = self.getPredicateToRun()

    self.query.start()
TRQK
  • 35
  • 6