
This video is only available to subscribers. Start a subscription today to get access to this and 484 other videos.
Preheating Images with Nuke
Episode Links
Adding Nuke to our project
We can add Nuke by adding this to our Podfile
:
pod 'Nuke'
And then running
$ pod install
Once installed, import it at the top of BeerListViewController.swift
:
import Nuke
Using Nuke to fetch images
We can use Nuke's shared Manager
class to fetch images for us and have those delivered to a Target
. Nuke already extends UIImageView
to conform to the Target
protocol, so usage is pretty simple:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// ...
let identifier = String(describing: BeerCell.self)
let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath) as! BeerCell
let beer = beers[indexPath.row]
let url = imageUrl(for: beer)
Nuke.Manager.shared.loadImage(with: url, into: cell.iconImageView)
// ...
}
The imageUrl(for:)
method is used to provide a stand-in image for beer records that don't have one. It uses placeimg.com for this.
private func imageUrl(for beer: Beer) -> URL {
return beer.iconImageUrl ?? URL(string: "https://placeimg.com/300/300/beer?id=\(beer.id)")!
}
It is important that we modify the URL in such a way that each beer has a deterministic, but unique URL. If we use the same URL for multiple beers, Nuke will cache the first one and use it for all of the other requests.
Preheating Image Requests
In our prefetchDatasource callback methods, we can determine which URLs we need to load and tell Nuke to preheat those images. These requests get fired at a lower-priority, but will often finish loading (and be cached) before this cell is displayed.
First we need a method that will transform a set of index paths into a set of image urls (provided the beers
array has an entry already for this row):
func imageRequests(indexPaths: [IndexPath]) -> [Nuke.Request] {
let beers: [Beer] = indexPaths.flatMap { indexPath in
guard indexPath.row < self.beers.count else { return nil }
return self.beers[indexPath.row]
}
let imageUrls = beers.map(self.imageUrl)
let requests = imageUrls.map(Nuke.Request.init)
return requests
}
Then we can pass those off to a preheater
, which is an object we need to instantiate and keep track of. Add a property at the top of the class:
let preheater = Nuke.Preheater()
Then we can use it when we are told to prefetch rows:
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
let requests = imageRequests(indexPaths: indexPaths)
preheater.startPreheating(with: requests)
// ...
}
When the user stops scrolling or changes direction, we'll see cancellations come in. We can pass these off and tell Nuke to stop preheating those requests. This is important because those requests are no longer the most important ones.
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
let requests = imageRequests(indexPaths: indexPaths)
preheater.stopPreheating(with: requests)
}
With this in place our images will seamlessly load, either by preheating, or by loading directly when the cell is requested. Nuke handles the case where a request is still in progress when an image is requested, and associates the image view with that request.