Implementing search with core data: Looking for info - Head First iPhone and iPad Development (2013)

Head First iPhone and iPad Development (2013)

Chapter 8. Implementing search with core data: Looking for info

image with no caption

It’s not enough to just be able to see data anymore. The era of big data is here and just being able to look at it doesn’t get you very far anymore. Now you probably won’t have a couple of petabytes on your phone (famous last words), but you will most likely have enough data that you’ll need to be able to sort and filter it to make it useful for your users. Core Data comes with some built-in functionality to slice through stacks of data and we’re going to show you how to use it!

The app is working, but it’s limited...

The first beta went over to HFN but it doesn’t have all the functionality they want. You’ve gotten the app to the point where it can hold and display episodes, and users can add their own. But once you get a big chunk of data in there, there’s a usability issue.

Imagine a giant list of TV shows and you’re trying to find just one....

HFN: HEAD FIRST NETWORK

Hi!

Thanks for getting that done—it looks great, but functionality is a little limited. We’d like our users to be able to do some data stuff:

§ Sort the data according to time or date

§ Search the data for keywords

Right now, once we have a lot of shows in there, it becomes tricky to find a particular episode or information about it. Adding search and sort would help a lot.

Thanks!

RELAX

We’re going to get to explaining the FetchedResultsController

We know we implemented a lot of ready-baked FetchedResults code at the end of the last chapter. We’re getting there, promise!

image with no caption

Jim: We’re going to need some kind of UI so that the user can get to that functionality.

Frank: That sounds like a good idea to me. What do we need to sort by?

Joe: Time and date.

Frank: So, only two options? How about a segmented control, that makes sense.

Jim: OK, what if we use one part of the segment for sorting by time and the other by date?

Frank: Based on each segment, you’ll need to create a new sort descriptor for the FetchedResultsController, too. One for date and one for time.

Joe: And then we can edit the fetch request on the FetchedResultsController and tell it to fetch again and reload the table.

Frank: Sounds good to me...

EXERCISE

Let’s jump into Xcode and get a segmented control in there and get it sorting. It’s actually pretty easy....

1. Go ahead and create the segmented control that Jim, Frank, and Joe were talking about.

In the main storyboard file, you’ll need to add the segmented control and update the titles to read “Show Time” and “Title”. Add an Action and an Outlet in MasterViewController.m for the segmented control.

image with no caption

2. Update the FetchedResultsController and implement the segmentControlValueChanged method in MainViewController.m.

The FetchedResultsController sort key needs to update based on the selection of the segmented control and then the table will refresh.

EXERCISE SOLUTION

Go ahead and get that segmented control in there and sorting. Once you get into it, the NSFetchedResultsController is really great at this sort of thing.

image with no caption

EXERCISE SOLUTION

image with no caption

image with no caption

That’s true, we did.

Honestly, we’ve probably built it up more than we should. It’s not very complicated.

The table gets populated like every other table we’ve used. Our cellForRowAtIndexPath method will be called looking for a UITableViewCell. We grab one and start configuring it. That’s where we use our FetchedResultsController. The NSFetchedResultsController is designed to support UITableViews. All we need to do is tell it how to find the data it needs...

Use an NSFetchRequest to describe your search

In order to tell the NSFetchedResultsController what we’re looking for, we need to create an NSFetchRequest. The NSFetchRequest describes what kind of objects we want to fetch, any conditions we want when it fetches them (like shows before 10 a.m.), and how Core Data should sort the results when it gives them back. Yeah, it does all that for us.

image with no caption

Let’s give it a shot...

TEST DRIVE

It works! Run the app and enter a few episodes. Tapping “Show Time” or “Title” changes the sort descriptor and the table updates accordingly. Nice work!

image with no caption

image with no caption

Exactly!

The search control comes with its own table view so we’ll give it its own FetchedResultsController but this time we’ll manipulate the predicate as the user types.

iOS 7 has Core Data and UIKit support for searching

Searching data used by an application is such a common use case that iOS 7 and Core Data provide just about everything we need to make it happen. Like nearly everything else we’ve done, we can split the visual presentation of the information (UIKit) from the actual searching of the data (Core Data). We’ll start with the UIKit part.

image with no caption

SearchDisplayController handles just about everything

iOS 7 includes a SearchBar and SearchDisplayController that provides us with all of the UIComponent classes we need. We can simply add the controller to our storyboard and that gets us our search bar, and a controller which gives us a modal table view that is filled with results. All we need to do is wire it up very much like our normal table views.

image with no caption

No, but our results controller can!

The results controller supports more than just the sort descriptor. We can use Core Data’s advanced searching and filtering capabilities with an NSFetchedResultsController to limit what data we get back. And that’s exactly what the UISearchDisplayController does.

Use predicates for filtering data

In database languages all over the world, predicates are used to scope (or limit) a search to only find data that matches certain criteria. Remember the NSFetchRequest we talked about earlier in the chapter? For that we used the Entity Information and Sort Descriptor, but we haven’t needed the predicate support...until now.

image with no caption

NSFetchRequest concepts are nearly identical to SQL

NOTE

SQL is a language used for managing databases.

The three major concepts in an NSFetchRequest are nearly identical to the expressions in standard SQL. Here’s what that would look like:

image with no caption

You wrap up the search constraints in an NSPredicate and give that to the NSFetchRequest. Let’s talk a little more about the predicate before we wire it all up.

The NSFetchRequest predicate controls what data is returned

NSPredicate is a deceptively simple class that lets us express logical constraints on our NSFetchRequest. You use entity and attribute names along with comparison operators to express your constraint information. You can create a basic NSPredicate with a string format syntax similar to NSString, like this:

fetchRequest.predicate = [NSPredicate predicateWithFormat:@" title contains[cd] %@", searchText];

But NSPredicates don’t stop with simple attribute comparisons. Apple provides several subclasses like NSComparisonPredicate, NSCompoundPredicate, and NSExpression, as well as a complex grammar for wildcard matching, object graph traversal, and more. You can even build complex fetch requests graphically in Xcode. There’s a whole predicate authoring guide available in Apple’s iOS documentation for all your searching needs.

The Search Bar lets you know what’s happening through a delegate

Once we add the searchDisplayController to the storyboard, Xcode will automatically hook up our controller to a SearchDisplayController property. Like almost all other iOS controls, the Search Display can be given a delegate and lets that delegate know what’s going on.

We need to react when the search bar changes. The search controller will ask us what it should do by invoking shouldReloadTableForSearchString when the search text changes. If we return YES from this, the search controller will refresh its results. The question is, how does it actually do the search?

SHARPEN YOUR PENCIL

What do you think we need to do next? Give it your best shot. What do we need so that the SearchDisplayController can actually execute searches against our Show database?

__________________________________________________

__________________________________________________

__________________________________________________

Answers in Core Data Cross Solution

DO THIS!

Drop a Search Bar and SearchDisplayControllerinto our storyboard

In our MasterView scene in the storyboard you should add the “Search Bar and Search Display Controller. “Add this right above the Prototype Cells section header. This will anchor a Search Bar at the top of our table view and add a display controller to the list of controllers at the bottom of the scene.

image with no caption

SHARPEN YOUR PENCIL SOLUTION

Here’s what we came up with for some next steps so that the SearchDisplayController can actually execute searches.

We can use an NSFetchedResultsController to execute the appropriate search based on the text in

the Search Bar. It just can’t be the same NSFetchedResultsController! __________________

____________________________________________________________________

It was a trick question...

The Search Controller doesn’t need to actually execute the search for us! The Search Bar and searchDisplayController wrap up a nice text field we can use for searching and a modal controller that shows a table view. Remember how our current table view works? Whenever the table view needs to fill in rows with data, it asks for a cell for that row. We grab a cell, populate it with the correct information, and give it back. Where do we get the information for the row? From our NSFetchedResultsController. The table view itself has nothing to do with how we get the data or which data we’re looking at.

We can use the same pattern here:

1. When the search bar changes, the SearchDisplayController will ask us if it should refresh its table view. We can respond with a YES, ensuring that it reloads the data in the table.

2. When it tries to refresh the table, its table view will start clamoring for rows. We can use a custom NSFetchedResultsController to grab results that match the current search bar contents.

BRAIN BARBELL

Now that you have an idea of what to do, go through the list below. Pick what we need to do to wire up the SearchDisplayController. There are multiple answers, so pick a bunch!

Conform to the UISearchDisplayDelegate protocol.

Implement the - (BOOL)searchDisplayController:(UISea rchDisplayController *)controller shouldReloadTa bleForSearchString:(NSString *)searchString method to tell the search controller is should always refresh the table.

Add a new NSFetchedResultsController that is configured based on the search terms.

Add a new tableview to show the results.

Create a new entity to store Search Matches.

Keep track of which tableview is visible so we know what to do in other methods

Create a new detail view controller to show details of selecting a search result item.

Create a property for the search results tableview.

Create a property for a new searchResultsFetchedController.

BRAIN BARBELL SOLUTION

Now that you have an idea of what to do, go through the list below. Pick what we need to do to wire up the Search Display Controller. There are multiple answers so pick a bunch!

Conform to the UISearchDisplayDelegate protocol.

This is so we get notified when the search text changes.

Implement the - (BOOL)searchDisplayController:(UISea rchDisplayController *)controller shouldReloadTa bleForSearchString:(NSString *)searchString method to tell the search controller is should always refresh the table.

This way we can reconfigure the FetchedResultsController when the search text changes. We’ll need to wipe out the old search controller so we’ll use the new one when the table reloads.

Add a new NSFetchedResultsController that is configured based on the search terms.

This will be cached in a property we’ll need to create (see below) and will have a predicate set on it based on the search text.

Add a new tableview to show the results.

This is handled by the SearchDisplayController.

Create a new entity to store Search Matches.

We’re loading shows here just like the main tableview.

Keep track of which tableview is visible so we know what to do in other methods

We need to know whether the main tableview is visible or the search results table view is visible so we know which search results controller to use among other things

Create a new detail view controller to show details of selecting a search result item.

We’ll need to tweak the prepareForSegue so that we grab the right row from the right tableview depending on which is visible, but we’re just using shows and our current detail view controller can handle those.

Create a property for the search results tableview.

The SearchDisplayController handles that and we can get the tableview from there.

Create a property for a new searchResultsFetchedController.

We’ll want to cache this and only re-create it (and redo the search) if the search text changes.

image with no caption

Yes! Great catch!

You’ll need to go through the various methods in our MasterViewController.m that deal with updating the table views to make sure you’re updating the right table view and that we’re using the right NSFetchedResultsController.

image with no caption

It’s not! Go grab the end of chapter code from the Git repo. You know what to do...

DO THIS!

To get the end of chapter code, go back and check out the master branch instead of the Chapter 8 code—you’ll get the finished project.

TEST DRIVE

Go ahead and check out the last of the code for the chapter. Once you have it in Xcode, take it for a spin!

image with no caption

HFN: HEAD FIRST NETWORK

To: Rock star developer

Awesome! We’re really psyched about this app. Now anybody who needs a Gilligan fix can get one quick.

Thanks!

SEARCH CROSS

Ha! It's not a word find... use the new words you learned here to solve the puzzle.

image with no caption

Across

Down

1. The SearchDisplayController uses a _______ view that shows a table with search results.

6. The SearchDisplayController uses a ______ field to use for searching.

7. The _________ describes the conditions that the entities must meet to be fetched.

8. The _______ descriptor tells Core Data how you want the data sorted before it’s sent back.

2. The search bar lets you know what’s happening through a _________.

3. The FetchedResultsController can be used to implement _________.

4. You can split the ______ presentation of the information from a search result from the searching of the data.

5. The FetchedResultsController uses NSFetchRequest to describe the _______ that we want to fetch.

8. NSFetchRequest concepts are very similar to _____.

SEARCH CROSS SOLUTION

Ha! It's not a word find... use the new words you learned here to solve the puzzle.

image with no caption

Across

Down

1. The SearchDisplayController uses a _______ view that shows a table with search results. [MODAL]

6. The SearchDisplayController uses a ______ field to use for searching. [TEXT]

7. The _________ describes the conditions that the entities must meet to be fetched. [PREDICATE]

8. The _______ descriptor tells Core Data how you want the data sorted before it’s sent back. [SORT]

2. The search bar lets you know what’s happening through a _________. [DELEGATE]

3. The FetchedResultsController can be used to implement _________. [SORTING]

4. You can split the ______ presentation of the information from a search result from the searching of the data. [VISUAL]

5. The FetchedResultsController uses NSFetchRequest to describe the _______ that we want to fetch. [OBJECTS]

8. NSFetchRequest concepts are very similar to _____. [SQL]

Your searching toolbox

You’ve got Chapter 8 under your belt and now you’ve added search and fetched results to your toolbox.

image with no caption