Waiting for a Spotlight Query to Finish (Spotlight and autocomplete)
After a long time of doing other things, I finally got around to working on semiBlog again. In that context, I was playing with integrating Spotlight (through NSMetadataQuery
). Now, NSMetadataQueries
are asynchronous, i.e. you create your query, you register a call-back method for when then query gives some results, and then you run the query. In other words, after the query starts running, the code doesn't halt and wait for the query to finish - instead, it just continues. Then, whenever the query returns some results, the call-back method is called. Looks like this:
- (void)someMethod { NSMetadataQuery *query = [[NSMetadataQuery alloc] init]; NSPredicate *pred = [NSPredicate predicateWithFormat: queryString]; [query setPredicate: pred]; [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(queryHandler:) name: NSMetadataQueryDidFinishGatheringNotification object: query]; [query startQuery]; // whatever comes now happens immediately: NSLog(@"something happens"); } - (void)queryHandler: (NSNotification *) inNotification { NSLog(@"The query is finished, do something."); // blabla... }
So far, so good. Now, what I want to do in semiBlog is integrate such a Spotlight query in the autocomplete of an NSTextView
. I can manipulate the autocomplete suggestions of NSTextView
by overriding completionsForPartialWordRange:indexOfSelectedItem:
(or by calling textView:completions:forPartialWordRange:indexOfSelectedItem:
in the delegate). Whenever autocomplete is initiated, these methods are called. The NSArray
they return determines what will show up in the list of completion suggestions. Looks like this:
- (NSArray *)completionsForPartialWordRange: (NSRange)charRange indexOfSelectedItem: (int *)index { return [NSArray arrayWithObjects: @"eins", @"zwei", @"drei", nil]; }Of course, when I want to use Spotlight here (e.g. I type "Knud", initiate autocomplete, Spotlight finds all contacts and events that somehow match the string "Knud", I use the result set for the completion suggestions), I run into problems. I can initiate the query in this method, but I will not get the result here, so that I can construct the return array from it. Instead, the result is handled in the asynchronous call-back. So, what to do? After a bit of searching, I found that run loops are what is needed here (see the documentation for
CFRunLoops
here). In short, from a current run loop I have to start a new run loop right after I initiate the query (calling CFRunLoopRun()
). The current will then wait until the new loop finishes (calling CFRunLoopStop(CFRunLoopGetCurrent ())
), which I will let it do at the end of the call-back, after the query results have been processed. So, now the code looks a little like this:
- (NSArray *)completionsForPartialWordRange: (NSRange)charRange indexOfSelectedItem: (int *)index { // get the current word. That will be the keyword in our query NSString *word = [[self string] substringWithRange: charRange]; // construct the query string from this word: NSString *queryString = @"someQueryString"; // construct the objects needed to perform the query NSMetadataQuery *query = [[NSMetadataQuery alloc] init]; NSPredicate *pred = [NSPredicate predicateWithFormat: queryString]; [query setPredicate: pred]; // register the call-back [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(queryHandler:) name: NSMetadataQueryDidFinishGatheringNotification object: query]; [query startQuery]; //start a new run loop CFRunLoopRun(); // after the new loop is finished, continue by returning the new suggestions: return [self suggestions]; } - (void)queryHandler: (NSNotification *) inNotification { NSMetadataQuery *query = [inNotification object]; NSArray *suggestions = // create the suggestions array from the query result [self setSuggestions: suggestions]; // stop the new run loop CFRunLoopStop(CFRunLoopGetCurrent ()); }
Now, I'm not sure I have grasped this run loop business completely - maybe there are some pitfalls here - , but the code seems to work! :-)
1 Comments:
A word of caution concerning the use of CFRunLoops.
Post a Comment
<< Home