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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Nuke Tutorial for iOS: Getting Started
30 mins
- Getting Started
- Setting up Nuke
- Installing with Swift Package Manager
- Using Nuke: Basic Mode
- Setting Loading Options
- Monitoring Memory Usage
- Using Instruments
- Advanced Nuking
- Loading with Requests
- Optimizing Code
- Using ImagePipeline to Load Images
- Caching Images
- Combining with Combine
- Setting up ImagePublisher
- Using ImagePublisher
- Chaining Requests
- Cleaning the Subscriber
- Where to Go From Here?
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.
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!