Bummer! This is just a preview. You need to be signed in with a Basic account to view the entire video.
Start a free Basic trial
to watch this video
Keeping an eye on data in a persistent store and using that data in a table view or collection view is such a common task that we have a class whose primary function is to mediate between the two. In this video, let's look at how the fetched results controller can make it much easier to display data in the table view.
-
0:00
Keeping an eye on data in a persistent store and
-
0:03
using that data in a table view or a collection view is such a common task
-
0:08
that we have a class whose primary function is to mediate between the two.
-
0:13
In our case we are adding to do items to the data store and
-
0:16
we want to display them in a table view.
-
0:18
For this, we can use an NSFetchedResultsController.
-
0:23
While table views can be used in several ways, fetch results controllers
-
0:26
are primarily intended for presenting data in a master list, exactly like we want.
-
0:32
UI table view expects its data source to provide cells
-
0:35
as an array of sections made up of rows.
-
0:38
We can configure a fetch results controller using a fetch request
-
0:42
that specifies the entity in any other optional parameters.
-
0:46
This fetch results controller then efficiently analyzes the results
-
0:50
of a fetch request and figures out how to order them in to sections and rows.
-
0:55
It also computes all the information for the indexes based on the results set.
-
1:00
This is really convenient,
-
1:01
it's also the most common way of using table views with core data.
-
1:05
So let's refactor our code to use a fetch results controller.
-
1:09
Now back in the ToDoListController,
-
1:13
We have CoreData imported up at the top which is good.
-
1:16
So let's add a new stored property, we don't need all these panes.
-
1:22
Okay, so here we're going to say lazy var
-
1:26
fetchedResultsController: this is of
-
1:31
type NSFetched, ResultsController.
-
1:36
And this is also generic class so it takes Item, that's our generic parameter.
-
1:42
Int, to set this up, we'll start by creating a fetch request.
-
1:49
So we'll say, let request, and in fact, we can just move this line of coding.
-
1:52
So copy paste the request, get it out of viewDidLoad and we'll put it in here.
-
2:00
Now remember, we can customize this fetch request includes sort descriptors and
-
2:04
additional predicates if needed.
-
2:06
We need this fetch request here, because the initializer for
-
2:10
NSFetchedResultsController takes a fetchRequest to fetch data.
-
2:14
In addition, it takes a managedObjectContext, and
-
2:17
also a thing called a section name keypath and a cache name.
-
2:21
So here we'll say let controller = NSFetchedResultsController, and
-
2:27
we'll use this initializer that takes a request.
-
2:30
A manageObjectContext, for which we'll need to say self,
-
2:33
and then for the last two we're gonna specify nil.
-
2:36
We'll return the controller as well.
-
2:41
The sectionNameKeyPath is used to precompute information about sections.
-
2:46
We include a key path on result objects that returns a section name.
-
2:50
Now we're not gonna be utilizing any of this for now so let's just pass nil in.
-
2:54
And then finally, it takes a cacheName.
-
2:57
And again here we're going to pass a nil to prevent caching.
-
3:00
Caches allow us to avoid the overhead of always performing fetch requests.
-
3:04
But again, not a concern for this course.
-
3:07
So earlier, we used the managed object context to execute the fetch request
-
3:11
ourselves.
-
3:12
Not any more, the fetch results controller is going to take care of that for us.
-
3:18
So in viewDidLoad let's get rid of this
-
3:21
lines of code, actually were just going to get rid of this line of code
-
3:24
the try managedObjectContext because we still need this do catch statement.
-
3:28
And now we're going to use the fetchedResultsController to do this.
-
3:32
So we'll say try fetchedResultsController.performFetch.
-
3:37
If we had run it at this point, nothing's going to happen, and that's because
-
3:40
the table view data source methods are now looking at the wrong data set for values.
-
3:45
We won't be assigning anything back to the item's stored property when we
-
3:48
use the fetchedResultsController.
-
3:50
So let's get rid of that and any associate lines so, var items out of here.
-
3:56
Okay, so now we're gonna have errors in our data source methods, that's okay.
-
4:00
So we'll start with a number of sections, the fetch results controller has
-
4:04
a sections property that contains information about the sections for
-
4:08
the receivers fetch results.
-
4:10
So here for number of sections we'll say return
-
4:14
fetchedResultsController.sections.count.
-
4:18
And you'll see that this is an optional property because there may not be a need
-
4:22
for multiple sections.
-
4:23
So if we don't have any section information in this controller, meaning
-
4:27
our fetch request failed potentially, then we'll use the nil coalescing operator,
-
4:32
and we'll say if this is new, then use 0.
-
4:34
Okay, so if there's a valid value for this, the nil coalescing operator's gonna
-
4:39
unwrap it and return it, otherwise we'll return 0.
-
4:43
Okay next is the number of rows in section.
-
4:46
The fetch results controller can contain many sections, much like a table view can.
-
4:51
So first, we need to get the relevant section to work with.
-
4:54
Now, notice that the data source method contains a parameter for section.
-
4:59
And as the table view is setting up its sections,
-
5:01
it's going to pass that argument through to this function.
-
5:04
In this case, in our case, it would just be one.
-
5:08
But we can use this section as an argument in the fetch results controller as well.
-
5:14
So here we'll say guard let section = fetchedResultsController.sections.
-
5:21
Now, this is an array of sections, so we'll use the subscript notation and
-
5:25
pass the argument through from the data source method
-
5:28
to get the current section that we want.
-
5:31
So when our tableView sets itself up, this is going to be 0.
-
5:34
Because we have one section, so
-
5:35
from this array of sections we're going to get the zeroth section.
-
5:39
Now again, this is an optional property so we need to put that question mark there.
-
5:44
And if that returns nil then we don't have any sections, so we don't have any rows so
-
5:49
it'll return 0.
-
5:52
Okay, once we have a section though, see if this guard statement works,
-
5:57
we can simply say return section, which is of type NSFetchedResultSectionInfo and
-
6:02
it has a property numberOfObjects.
-
6:05
So, hopefully that makes sense.
-
6:06
We grab a section out of the fetchedResultsController.
-
6:09
That section contains information about what goes into each row and
-
6:12
we're just going to call the number of objects property on it.
-
6:15
For the final bit, we need to customize each cell.
-
6:18
Remember that a fetchedResultsController can compute
-
6:21
all the information about rows and sections using an index path.
-
6:25
So, let's create a private helper that takes a cell object and
-
6:30
an index path and returns a configured cell.
-
6:33
So down here we'll say private func configureCell and
-
6:40
this is going to be of type UITableViewCell, atIndex path,
-
6:46
or we'll just say at indexPath.
-
6:50
I'm saying just at because the argument type is specific enough that it's obvious
-
6:55
what at means.
-
6:57
And then this method is going to return fully configured UITableViewCell instance.
-
7:04
Now inside this method the first thing we need to do configure a cell is
-
7:08
an item object.
-
7:09
So we say let item = fetchResultsController.
-
7:13
And it has a handy method object(at: indexPath), so
-
7:16
again pass the indexPath through.
-
7:18
When we created the fetchedResultsController, we set it up.
-
7:21
This is a generic type, we gave it a generic parameter of item.
-
7:25
So calling this method returns an instance of item, so
-
7:28
we don't need to do any casting.
-
7:30
Now, we can assign the item's text to the cell's text label and return it.
-
7:34
So we'll say cell.textLabel.text = item.text and return cell.
-
7:43
Back inside cell for an indexPath,
-
7:46
once you grab the cell all we'll do is say, we'll get rid of all of this.
-
7:52
We'll say return, configure cell we'll pass the cell through and the indexPath.
-
8:00
Run the app now, and you should see the new data displayed.
-
8:05
Also notice that it is sorted, perfect, but if you tried to add a new task,
-
8:09
you'll see that we still run into the issue where adding a new item
-
8:13
is not going to trigger a change.
-
8:15
Wait, what, I thought the fetchedResultsController handled that?
-
8:18
It does but we still need to do a few more things.
-
8:20
Let's tackle that in the next video.
You need to sign up for Treehouse in order to download course files.
Sign up