Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

iOS

Andrew Boryk
Andrew Boryk
15,916 Points

How to Query with a Predicate for a UISearchBar?

I am creating a search bar so that users on my app can search for other users. I have everything hooked up, and it works fine because I would filter an array of PFUsers using a predicate. However, when recently I realized there was a cap for the number of users one can query, I figure I would make adjustments down the line. I recently found a query function that I thought would work. Here is my code:

<code> -(void)filterContentForSearchText:(NSString *)searchText scope:(NSString *)scope{ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(username CONTAINS[cd] %@)", searchText]; // How I used to set it: // self.searchResults = [self.contacts filteredArrayUsingPredicate:predicate]; PFQuery *query = [PFQuery queryWithClassName:@"User" predicate:predicate]; [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) { if (error) { NSLog(@"Error %@ %@", error, [error userInfo]); } else{ self.searchResults = objects; } }]; } </code>

And through the "PFQuery *query = [PFQueryWithClassName...." line, my app terminates and I receive the output:

<strong>Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Regex queries are not supported with [PFQuery queryWithClassName:predicate:]. Please try to structure your data so that you can use an equalTo or containedIn query.'</strong>

Please help if you can, for now I will just go back to my original setup. Sorry if the formatting is off, I'm just assuming Markdown is the same as HTML. Thank you!!

1 Answer

Robert Bojor
PLUS
Robert Bojor
Courses Plus Student 29,439 Points

Hi Andrew,

I have implemented a UISearchBar in one of my projects and here's how I used its delegate methods ( code below ). As a side note, I am receiving book details from Parse and the details are saved into a books array.

Once the query finishes and all the results have arrived the other array filteredBooksArray is created as a copy of books. This will ensure that we have an original copy of the books array and the filtered array can be modified without any worries. The table listing the results is using the filtered array instead of the original to drive the results.

-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
    self.searchTerm = searchText;
}

-(void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
    [self.bookSearchBar resignFirstResponder];
    //reset to the original array and reload table
    self.filteredBooksArray = self.books;
    [self.booksTable reloadData];
}

-(void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
    [self.bookSearchBar resignFirstResponder];
    if ([self.searchTerm isEqualToString:@""]) {
        // Nothing to do here - Reset to the original array and reload table
        self.filteredBooksArray = self.books;
    } else {
        // Filter the array based on the term searched for using a predicate
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.title contains[c] %@",self.searchTerm];
        self.filteredBooksArray = [NSMutableArray arrayWithArray:[self.books filteredArrayUsingPredicate:predicate]];
    }
    [self.booksTable reloadData];
}

-(void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
    self.filteredBooksArray = self.books;
    [self.booksTable reloadData];
}

Here's a similar piece of code but using a normal UITextField instead of a UISearchBar. However, I would recommend using the UISearchBar since it has those nice delegate methods - I was "forced" by the client to use an UITextField in his app.

The principle is the same, keep the original array untouched and work on a copy. When you want to reset the search results just reset the copy to the original array and reload the table. Just remember to use the copy array to drive the results in the table.

_rightMenuSearchTerm = _rightMenuSearchBar.text;
if ([_rightMenuSearchTerm isEqualToString:@""]) {
    _filteredRightMenuItems = _rightMenuItems;
} else {
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name contains[c] %@", _rightMenuSearchTerm];
    _filteredRightMenuItems = [NSMutableArray arrayWithArray:[_rightMenuItems filteredArrayUsingPredicate:predicate]];
}
[_rightMenuTable reloadData];
Andrew Boryk
Andrew Boryk
15,916 Points

Robert,

Thank you so much. I have my Search Bar set up this way as well. Is there any way you could tell me how you are filling your self.books array?

Robert Bojor
Robert Bojor
Courses Plus Student 29,439 Points

It's a simple Parse query and based on the results...

for (PFObject *object in objects) {
    Book *book = [[Book alloc] init];
    book.bookID = object.objectId;
    book.ISBN = object[@"isbn"];
    book.title = object[@"title"];
    book.author = object[@"author"];
    book.printHouse = object[@"printHouse"];
    book.publishingYear = object[@"publishingYear"];
    book.genre = object[@"genre"];
    book.bookCount = (int)[object[@"bookCount"] integerValue];
    book.description = object[@"description"];
    book.isBorrowed = (BOOL)object[@"isBorrowed"];
    book.isRead = (BOOL)object[@"isRead"];
    book.isLended = (BOOL)object[@"isLended"];
    if (![object[@"coverImage"] isKindOfClass:[NSNull class]]) {
        book.coverImage = object[@"coverImage"];
    } else {
        book.coverImage = nil;
    }
    [self.books addObject:book];
}
if ([self.searchTerm isEqualToString:@""]) {
    self.filteredBooksArray = self.books;
} else {
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.title contains[c] %@",self.searchTerm];
    self.filteredBooksArray = [NSMutableArray arrayWithArray:[self.books filteredArrayUsingPredicate:predicate]];
}
[self.booksTable reloadData];
Andrew Boryk
Andrew Boryk
15,916 Points

Just what I needed to visualize. Thank you!