Nuke Tutorial for iOS: Getting Started

In this Nuke tutorial, you’ll learn how to integrate Nuke using Swift Package Manager and use it to load remote images, both with and without Combine. By Ehab Amer.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 4 of 4 of this article. Click here to view the first page.

Chaining Requests

So far, you have one publisher defined, but you want to use two publishers — one for each image request — and chain the two to execute them sequentially.

In PhotoViewController.swift within loadImage(url:), right after resizedImagePublisher is defined, add this line.

let originalImagePublisher = ImagePipeline.shared.imagePublisher(with: url)

This creates a publisher directly using the image URL without any image processors.

Next, replace:

cancellable = resizedImagePublisher
  .sink(

with:

cancellable = resizedImagePublisher.append(originalImagePublisher)
  .sink(

The addition of .append(:) on a publisher creates a new publisher that combines resizedImagePublisher and originalImagePublisher. Thus, you can treat it the same way, and internally it will make each publisher work and finish before going to the next one.

Build and run. Tap any image from the gallery and see it opening with an image, then in front of your eyes showing a sharper image.

Sharper image replacing the existing one.

Note: If you don’t see the transition clearly, reduce the image size you are requesting in the gallery in PhotoGalleryViewController.resizedImageProcessors to reduce the quality of the first image.

Cleaning the Subscriber

Earlier you learned that in the subscriber, you get notified once with the result of the publisher if it finished or failed, and only if it finished do you get notified with a value.

This means that you are responding in two different places, although all you are doing is just showing a different image and a different content mode.

Why not make the publisher just provide the image and the content mode to use, so all you have to do is just display those values. More accurately: Make the publisher provide a value even if it failed.

The publisher you are using returns an object that has the image among other things but doesn’t have anything about the content mode. That is something unique to this app.

So the first thing you want to do is tell the publisher to provide a different value type, instead of ImageResponse, which is a type defined in ImagePublisher. You want to use a tuple (Image, UIView.ContentMode)

Back to PhotoViewController. Right before the call to .sink(receiveCompletion:receiveValue:) add the following:

.map {
  ($0.image, UIView.ContentMode.scaleAspectFill)
}

map(_:) does a transformation for you. You are using the original value received from the publisher and converting this value to a tuple. And since there is a value, this means the publisher succeeded, and all the NASA images should have scaleAspectFill as their content mode.

Second, you want to intercept the publisher if an error occurred that would cause it to provide a failure. Instead, make the publisher give you the failure image and aspect fit in a tuple.

After the definition of originalImagePublisher, add:

guard let failedImage = ImageLoadingOptions.shared.failureImage else {
  return
}

Then, right after the closing braces for map(_:) and before the call to .sink(receiveCompletion:receiveValue:), add:

.catch { _ in
  Just((failedImage, .scaleAspectFit))
}

The publisher calls .catch(_:) whenever it fails. The call to Just(_:) creates a publisher that always sends the value given and then completes. In this case, it’s the default failure image you specified in the global ImageLoadingOptions. The catch(_:) means that any error from the image publishers will be “caught” and then the failure replaced with the failure image.

Finally, replace all the code you have for .sink(receiveCompletion:receiveValue:) with:

.sink {
  self.imageView.image = $0
  self.imageView.contentMode = $1
}

Your subscriber will always receive a value because you managed to transform the publisher to one that never fails. And the result type it now provides is much more convenient for your app. So naturally, the code that is checking if it succeeded or not is not needed, and all you need is “Just” receive the value. :]

Build and run the app. It will work just as before, but now you have some simpler Combine code!

Where to Go From Here?

Congratulations on making it this far! You’ve learned quite a bit about how to use Nuke to improve your app’s performance!

Download the completed project files by clicking the Download Materials button at the top or bottom of the tutorial.

There are still more features in Nuke you can explore. For instance, Nuke has support for animated GIFs. You can also go deeper into the cache management topic, if your app needs that level of control.

Additionally, several plug-ins can extend the functionality of Nuke, such as support for SwiftUI , RxSwift, WebP, and Alamofire.

And you can always learn more about Combine from the tutorial Combine: Getting Started.

If you enjoyed learning about Combine, then you might be interested in our Combine: Asynchronous Programming with Swift book.

I hope you enjoyed this Nuke tutorial and, if you have any questions or comments, please join the forum discussion below!