Strategy for live searching using background thread on iOS

44 views
Skip to first unread message

Brendan Duddridge

unread,
Aug 31, 2016, 4:00:27 PM8/31/16
to Couchbase Mobile
Hi,

In the current version of my iOS app I've disabled live search. So you have to type in your search term then tap on the Search button. 

My app then fetches the records on the main thread and displays the results to the customer.

However, I would like to use live search so that as they tap on the keys, my app filters the records and displays them.

The problem is because the searching is done on the main thread, the response while typing is "jerky" with delays as you type each character.

In a previous version of my app, I used the following code to fetch in a background thread using an NSOperationQueue:

- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {


        self.savedSearchTerm = searchText;

       __weak FormEntryListViewController *weakSelf = self;

        [self.searchQueue cancelAllOperations];

       [self.searchQueue addOperationWithBlock:^{

                dispatch_async(dispatch_get_main_queue(), ^{

                       [weakSelf displayLoadingHud];

               });


                // Ensure you're assigning to a local variable here.

               // Do not assign to a member variable.  You will get

               // occasional thread race condition related crashes

               // if you do.


                NSMutableDictionary *results = [weakSelf freshFormEntries];


                [[NSOperationQueue mainQueue] addOperationWithBlock:^{


                        // Modify your instance variables here on the main

                       // UI thread.


                        [weakSelf.formEntries removeAllObjects];

                       [weakSelf.formEntries addEntriesFromDictionary:results];

                       [weakSelf loadRecords];


                        [MBProgressHUD hideHUDForView:weakSelf.view animated:YES];


                }];


        }];


}


So as you type in a search term, previous in-progress searches are cancelled and new ones are created until finally the user stops typing and the results are displayed on the main thread. It was fast and smooth.

However, this technique doesn't really work with Couchbase Lite because I cannot access the model objects on the main thread those objects that I fetched on the background thread using the NSOperationQueue.

The only thing I can think of is fetching document IDs from the search results, then fetching again all of the model objects for those IDs in one big fetch. The problem with this is there could potentially be thousands of results and now I'm fetching twice.

FYI, I'm using SQLite's FTS for the searching to make searching quick. But it still doesn't help to prevent the 

Is there a better way?

Thanks,

Brendan

Jens Alfke

unread,
Aug 31, 2016, 4:53:17 PM8/31/16
to Couchbase Mobile

On Aug 31, 2016, at 1:00 PM, Brendan Duddridge <bren...@gmail.com> wrote:

The only thing I can think of is fetching document IDs from the search results, then fetching again all of the model objects for those IDs in one big fetch. The problem with this is there could potentially be thousands of results and now I'm fetching twice.

That sounds good — it wouldn't be fetching twice. A query does not load the document, only the key/value pair and docID from the index.

—Jens

Brendan Duddridge

unread,
Aug 31, 2016, 5:42:45 PM8/31/16
to Couchbase Mobile
Ah ok. Thanks Jens!

Scott Ahten

unread,
Sep 16, 2016, 10:10:23 AM9/16/16
to Couchbase Mobile


On Wednesday, August 31, 2016 at 4:00:27 PM UTC-4, Brendan Duddridge wrote:

However, this technique doesn't really work with Couchbase Lite because I cannot access the model objects on the main thread those objects that I fetched on the background thread using the NSOperationQueue.

One technique is to create what is known as a view model from each query row or CBLite model object. View models are usually subclasses of NSObject and have the properties you would use to display search results in our UI, such as strings, UIImages, etc. As such, you can pass them between threads. The view model would have a documentID property for the original document so you could actually retrieve it when the user selects it. For example, the view model would contain the display name of the result, possibly an thumbnail, etc. Whatever you would normally use to display search rows. Another advantage is that if you have a non-homogeneous search you can use the same view model class to display the results for multiple document types. You could have different initializers for each document type or a single initializer that checks the row values / document type and sets the properties appropriately. 

So, you would loop though the resulting rows or CBLModels and build view models from them, then send them to the main thread for display. You can make them immutable or possibility even reuse them if you find the query creates a large number of objects. If you can create view models from query row values alone, as opposed to CBLModels, you can avoid having to load the document at all until the user actually selects it in the search results.  

Reply all
Reply to author
Forward
0 new messages