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
Now that we have a way of downloading data, let's update the AlbumListDataSource to fire off requests for the artwork once the initial cell set up has been completed
-
0:00
To use our operation and
-
0:01
get image data we're going to add come code to the album list data source.
-
0:05
So let's navigate there.
-
0:08
After a cell has been set up with the initial data in self for
-
0:12
row index path we'll fire off a request for the album artwork.
-
0:16
This way we know that the user now sees actual album data on screen and
-
0:21
in the background we're going to let this request process and
-
0:24
hopefully reload with a new Artwork.
-
0:26
So in here we'll say, if album.artworkstate = placeholder.
-
0:33
Remember we are only doing this,
-
0:35
we are only going to fire off a request if the current state is a placeholder,
-
0:38
indicating that we haven't attempted to download the image yet and
-
0:42
the operation is neither failed nor completed successfully.
-
0:45
So inside this if statement, we're going to call a new method.
-
0:49
And let's go ahead and define that at the bottom.
-
0:52
We'll say func downloadArtworkForAlbum.
-
0:59
And this is going to be an instance of album.
-
1:05
In this method, we're going to create an operation using the album,
-
1:10
add it to the operation queue, and execute the logic.
-
1:13
Remember though that we also want to keep track of the actual row
-
1:17
which this album is in.
-
1:19
Why is that again?
-
1:20
When we fire off a download for the artwork, is that row scrolls off screen,
-
1:25
there's no reason to continue the download and we would have to cancel it.
-
1:30
In addition, if we reloading a table view and
-
1:32
we already have an operation processing in the background,
-
1:36
to download artwork for that particular row we need to know that.
-
1:40
We need to keep track of that so we don't create a duplicate operation and
-
1:44
do the exact same work over again.
-
1:46
Now, we need to keep track of this in a very simple way.
-
1:50
And one easy way of getting this done is associating a download operation
-
1:54
with a rows index path.
-
1:56
So we'll say, downloadArtworkForAlbum atIndexPath and
-
2:02
we include the IndexPath as an argument.
-
2:05
To keep track of the operations and to create a queue, let's create a new type.
-
2:10
So in networking, again New File, swift file,
-
2:13
and we'll name this Pending Operations.
-
2:19
In here we're going to define a type with two simple stored properties.
-
2:23
So we'll create a class called PendingOperations, that contains
-
2:28
a dictionary, we'll name it downloadsInProgress.
-
2:32
This is a variable.
-
2:34
And the type is index path to operation.
-
2:40
And we'll just create an empty dictionary to start out.
-
2:41
And then we also want a download queue.
-
2:45
And this is an instance of operation queue.
-
2:51
So the first property here is a dictionary that keeps track of operations created and
-
2:55
the index paths they're associated with.
-
2:58
To figure out what operations are associated with an index path,
-
3:02
we can simply use the path as a key in this dictionary.
-
3:05
The second property is an instance of operation queue.
-
3:08
And this is the operation queue we're going to be adding our operations to.
-
3:12
We are not going to customize this in any way.
-
3:15
Now a couple of videos ago I mentioned that within operation queue, you could
-
3:18
customize it by defining the max number of operations executed at any time or
-
3:24
at the same time.
-
3:25
And we do this by sending a value on the max concurrent operation current property.
-
3:30
By leaving it as its default value,
-
3:32
we're going to let the system determine how many concurrent operations it can
-
3:37
execute simultaneously which honestly is the best approach.
-
3:41
Okay, so now let's use this very simple type back in the album list data source.
-
3:46
First, we need access to the dictionary to keep track of the operations we create or
-
3:52
as well as the operation queue to add the operations.
-
3:55
So let's add an instance of that new type as stored property,
-
3:59
let pendingOperations and we'll create an instance.
-
4:03
So inside this method.
-
4:06
We'll first start by checking to see if we've already created and
-
4:10
added an operation for a given row.
-
4:12
And the way we're going to do that, is, we'll check if we get a value back
-
4:17
by using this index path as a key in that dictionary.
-
4:21
So we'll say, if let, and we'll omit the name.
-
4:23
Because we don't care about the operation.
-
4:25
We just want to know if it's in the actual dictionary.
-
4:29
We'll say if let _ = pendingOperations.downloadsInProgress,
-
4:33
and using subscript notation, we'll use the indexPath argument as a key.
-
4:38
Now, if there is an operation,
-
4:41
if this succeeds, then we enter the if let statement.
-
4:44
It unwraps that operation.
-
4:46
We don't care about it, now that we know though,
-
4:48
that it does exist, that means we've already created an operation for
-
4:52
this cell, for this row, and it's currently processing in the background.
-
4:56
So we're not going to do anything else, and we'll return over here.
-
5:00
If we haven't though, if this if let statement fails,
-
5:03
that means that we do not have an operation for this album, this indexPath.
-
5:09
So now let's start the real work,
-
5:11
let's create an instance of the downloader with an album for that cell.
-
5:16
So let downloader = ArtworkDownloader, and it takes an album,
-
5:21
we'll pass the argument through.
-
5:24
So remember all the work we want to do is encapsulated in the main method
-
5:28
of the operation sub class,
-
5:30
including assigning the image back to this album instance.
-
5:35
When the operation completes, the album will then have an image and
-
5:38
we can display that image.
-
5:40
To do, however, we need to trigger the table views reload process so
-
5:45
that it can go through the data source methods again and
-
5:48
assign the album artwork we've fetched to the cells image view.
-
5:52
There are two things to consider here.
-
5:54
First off, how do we know when an operation completes?
-
5:57
And second, why should we reload the entire table view,
-
6:00
just to set up one row again with artwork?
-
6:03
If we do that the table view would reload every millisecond,
-
6:06
basically, as each individual download operation completed, because remember,
-
6:10
we're creating one for every single row.
-
6:13
Luckily, both these things are trivial to solve.
-
6:16
When an operation completes,
-
6:18
it executes a closure assigned to a store property called completion block.
-
6:23
So, any code that we want to execute when an operation is complete
-
6:27
we define in a closure and then assign it to that stored property.
-
6:30
So, I say downloader.completionBlock and to this we'll assign a closure.
-
6:35
Like everything we do with an operation
-
6:38
we first want to check if the operation is cancelled.
-
6:41
Even in here.
-
6:42
Because even after the operation is complete
-
6:45
we could still go ahead and cancel it.
-
6:47
So in your mind, think of it this way.
-
6:49
There's an operation running in the background.
-
6:51
It gets the image, it assigns it back to the album.
-
6:54
And then after that happens but before it reloads, the user scrolls off screen.
-
6:58
In that case, well we potentially could cancel it.
-
7:01
So in here we need to check if it's cancelled.
-
7:03
We'll say if downloader is cancelled then again,
-
7:09
we'll just return out of the block and do nothing.
-
7:12
If our operation wasn't cancelled, we'll just finish up by removing this operation
-
7:17
from our dictionary because since we're tracking active operations and
-
7:21
this is completed, it doesn't need to stay in that dictionary anymore.
-
7:25
And then we'll reload the table view.
-
7:27
The reloading of the table view is UI code so it needs to be done on the main thread
-
7:31
because remember the operation is executed on a dispatch queue in the background.
-
7:36
So here we'll call DispatchQueue.main.async.
-
7:40
And in here we want to indicate that the operation is done for
-
7:43
the purposes of tracking.
-
7:45
So it'll remove it from our dictionary.
-
7:46
We'll say self.pendingOperations.downloadsInProgres-
-
7:51
s.removeValue ForKey and we'll use the indexPath again.
-
7:55
That way when it's reloaded, if the previous operation was complete.
-
7:59
And let's assume that we don't have that artwork state property that
-
8:03
monitors whether we've done that or not.
-
8:05
The next time we come along, since we've removed it already.
-
8:08
This would fail and we would go through the process again.
-
8:12
After removing this operation, we need to reload the row so
-
8:15
that we run through the set up again and assign the right image.
-
8:19
Rather than reloading the entire table view,
-
8:21
we need to make sure we reload just the row whose operation completed.
-
8:25
And again, that's easy.
-
8:28
And we'll do self.tableview and the table view has a method reload rows.
-
8:34
And it takes an array of index paths.
-
8:38
So our paths are current index path in.
-
8:40
And then the animation down and then we say dot automatic.
-
8:44
It takes this array of index paths and reloads just those rows by calling
-
8:49
the data source methods and passing in the selective set of index paths.
-
8:53
Now, we have an error here.
-
8:54
And that's because we don't have a reference to the table view
-
8:58
outside of a data source method.
-
8:59
So, calling self.tableView is just going to return an error.
-
9:03
Well some of this by adding a stored property up at the top,
-
9:07
to hold on to our table view, so
-
9:10
let tableView UITableView, and then we'll modify our Init method.
-
9:15
So, we'll say tableView UItable view and
-
9:20
self dot table view equal table view.
-
9:26
Now let's go back down to our mtethod and it should work so there we go.
-
9:30
And this wraps up the code we want to execute when the operation is complete.
-
9:36
But remember, at no point here have we added the operation to the queue, nor
-
9:40
have we added the operation to this downloadsInProgress dictionary to keep
-
9:44
track of it.
-
9:45
This part is easy, though.
-
9:47
So first to add it to the dictionary, we'll say pendingOperations or
-
9:52
rather we'll add it to the queue first, dot download queue.addOperation.
-
9:59
And we'll pass in the downloader, pass through the downloader.
-
10:03
So that's all we need to do on our end.
-
10:04
The queue now keeps track of the operations and
-
10:06
executes them in a particular order when they hit that ready state.
-
10:10
We need to add one more line of code here and
-
10:12
then is to track the operation we just added.
-
10:15
So, pendingOperations.downloadsInProgress and using indexPath
-
10:21
as a key will assign the downloader which is our operation as a value.
-
10:26
When we add the operation to the queue we keep track of it in our dictionary.
-
10:30
When we call this method at the top,
-
10:32
we'll check that there is an operation in progress for the particular index path.
-
10:38
If there is, well then we don't do anything else.
-
10:41
But if there isn't, we start this process all over.
-
10:44
When the operation is done, as indicated in our completion block,
-
10:47
we'll remove that particular key rally pair from the dictionary.
-
10:50
Now, we went ahead and modified our init method, so
-
10:53
we will have an error in the album list controller dot.
-
10:57
And that's up at the top here in our data source because we now need a table view.
-
11:05
So, let's fix that.
-
11:06
We'll say, lazy var dataSource.
-
11:09
And we'll do this all over again.
-
11:11
So we'll copy this.
-
11:13
We'll again assign an executing closure.
-
11:16
So AlbumListDataSource =, we'll clone the closure and inside we'll say return, and
-
11:23
this time we're still going to initialize it with an empty array to start off.
-
11:27
But now, we can pass through self.tableView.
-
11:31
We're not done yet as always.
-
11:34
If you run the app now, you still won't see any album artwork.
-
11:37
But that's a simple fix, and
-
11:39
we'll wrap it up with the rest of the project in the next video.
You need to sign up for Treehouse in order to download course files.
Sign up