Swiftpack.co - Package - kean/Nuke

Powerful Image Loading System

Upgrading from the previous version? Use a Migration Guide.

Nuke provides a simple and efficient way to download and display images in your app. Behind its clear and concise API is an advanced architecture which enables its unique features and offers virtually unlimited possibilities for customization.

Fast LRU memory and disk cache · Smart background decompression · Image processing · Resumable downloads · Intelligent deduplication · Request prioritization · Prefetching · Rate limiting · Progressive JPEG, WebP · Animated images · Alamofire, WebP, Gifu, FLAnimatedImage integrations · Reactive extensions


Getting Started

Nuke is easy to learn and use. Here is an overview of its APIs and features:

To learn more see a full API Reference, and check out the demo project included in the repository. When you are ready to install, follow the Installation Guide. See Requirements for a list of supported platforms.

To learn about the image pipeline itself, see the dedicated section:

If you'd like to contribute to Nuke see Contributing.


Image View Extensions

Download and display an image in an image view with a single line of code:

Nuke.loadImage(with: url, into: imageView)

Nuke will check if the image exists in the memory cache, and if it does, will instantly display it. If not, the image data will be loaded, decoded, processed, and decompressed in the background.

See Image Pipeline Overview to learn more.

In a List

When you request a new image for the existing view, Nuke will prepare it for reuse and cancel any outstanding requests for the view. Mangaging images in lists has never been easier.

func collectionView(_ collectionView: UICollectionView,
                    cellForItemAt indexPath: IndexPath)
                    -> UICollectionViewCell {
    Nuke.loadImage(with: url, into: cell.imageView)
}

The requests also get canceled automatically when the views are deallocated. Call Nuke.cancelRequest(for: imageView) to cancel the request manually.

Placeholders, Transitions, Content Modes

Use ImageLoadingOptions to set a placeholder, select one of the built-in transitions, or provide a custom one.

let options = ImageLoadingOptions(
    placeholder: UIImage(named: "placeholder"),
    transition: .fadeIn(duration: 0.33)
)
Nuke.loadImage(with: url, options: options, into: imageView)

You can even customize content modes per image type:

let options = ImageLoadingOptions(
    placeholder: UIImage(named: "placeholder"),
    failureImage: UIImage(named: "failureImage"),
    contentModes: .init(success: .scaleAspectFill, failure: .center, placeholder: .center)
)

In case you want all image views to have the same behavior, you can modify ImageLoadingOptions.shared.

ImageRequest

ImageRequest allows you to set image processors, change the request priority and more:

let request = ImageRequest(
    url: URL(string: "http://..."),
    processors: [ImageProcessor.Resize(size: imageView.bounds.size)],
    priority: .high
)

The advanced options available via ImageRequestOptions. For example, you can provide a filteredURL to be used as a key for caching in case the URL contains transient query parameters.

let request = ImageRequest(
    url: URL(string: "http://example.com/image.jpeg?token=123")!,
    options: ImageRequestOptions(
        filteredURL: "http://example.com/image.jpeg"
    )
)

There are more options available, to see all of them check the inline documentation for ImageRequestOptions.


Image Processing

Nuke features a powerful and efficient image processing infrastructure with multiple built-in processors including ImageProcessor.Resize, .Circle, .RoundedCorners, .CoreImageFilter, .GaussianBlur.

This and other screenshots are from the demo project included in the repo.

Resize

To resize an image, use ImageProcessor.Resize:

ImageRequest(url: url, processors: [
    ImageProcessor.Resize(size: imageView.bounds.size)
])

By default, the target size is in points. When the image is loaded, Nuke will scale it to fill the target area maintaining the aspect ratio. To crop the image set crop to true.

There are a few other options available, see ImageProcessor.Resize documentation for more info.

GaussianBlur, Core Image

ImageProcessor.GaussianBlur blurs the input image. It is powered by the native CoreImage framework. To apply other filters, use ImageProcessor.CoreImageFilter:

ImageProcessor.CoreImageFilter(name: "CISepiaTone")

For a complete list of Core Image filters see Core Image Filter Reference.

Custom Processors

Custom processors need to conform to ImageProcessing protocol:

public protocol ImageProcessing {
    var identifier: String { get }
    var hashableIdentifier: AnyHashable { get }

    func process(image: Image, context: ImageProcessingContext?) -> Image?
}

The process method is self-explanatory. identifier: String is used by disk caches, and hashableIdentifier: AnyHashable is used by memory caches for which string manipulations would be too slow.

For one-off operations, use ImageProcessor.Anonymous to create a processor with a closure.

Smart Decompression

When you instantiate UIImage with Data, the data can be in a compressed format like JPEG. UIImage does not eagerly decompress this data until you display it. This leads to performance issues like scroll view stuttering. To avoid these it, Nuke automatically decompresses the data in the background. Decompression only runs if needed, it won't run for already processed images.

See Image and Graphics Best Practices to learn more about image decoding and downsampling.


Image Pipeline

At the core of Nuke is the ImagePipeline class. Use the pipeline directly to load images without displaying them:

let task = ImagePipeline.shared.loadImage(
    with: url,
    progress: { _, completed, total in
        print("progress updated")
    },
    completion: { result: Result<ImageResponse, ImagePipeline.Error> in
        print("task completed")
    }
)

In some cases, you only want to download the data without doing any expensive decoding or processing. You can do that with loadData(with:progress:completion:).

ImageTask

When you start the request, the pipeline returns an ImageTask object, which can be used for cancelation and more.

task.cancel()
task.priority = .high

Customize Image Pipeline

If you want to build a system that fits your specific needs, you won't be disappointed. There are a lot of things to tweak. You can set custom data loaders and caches, configure image encoders and decoders, change the number of concurrent operations for each individual stage, disable and enable features like deduplication and rate limiting, and more.

To learn more see the inline documentation for ImagePipeline.Configuration and Image Pipeline Overview.

Here are some of the protocols which can be used for customization:

  • DataLoading – Download (or return cached) image data
  • DataCaching – Store image data on disk
  • ImageDecoding – Convert data into images
  • ImageEncoding - Convert images into data
  • ImageProcessing – Apply image transformations
  • ImageCaching – Store images into a memory cache

The entire configuration is described by the ImagePipeline.Configuration struct. To create a pipeline with a custom configuration either call the ImagePipeline(configuration:) initializer or use the convenience one:

let pipeline = ImagePipeline {
    $0.dataLoader = ...
    $0.dataLoadingQueue = ...
    $0.imageCache = ...
    ...
}

And then set the new pipeline as default:

ImagePipeline.shared = pipeline

Default Image Pipeline

The default image pipeline is initialized with the following dependencies:

// Shared image cache with a size limit of ~20% of available RAM.
imageCache = ImageCache.shared

// Data loader with a default `URLSessionConfiguration` and a custom `URLCache`
// with memory capacity 0, and disk capacity 150 MB.
dataLoader = DataLoader()

// Custom aggressive disk cache is disabled by default.
dataCache = nil

// By default uses the decoder from the global registry and the default encoder.
makeImageDecoder = { context in ImageDecoderRegistry.shared.decoder(for: context) }
makeImageEncoder = { _ in ImageEncoder() }

Each operation in the pipeline runs on a dedicated queue:

dataLoadingQueue.maxConcurrentOperationCount = 6
dataCachingQueue.maxConcurrentOperationCount = 2
imageDecodingQueue.maxConcurrentOperationCount = 1
imageEncodingQueue.maxConcurrentOperationCount = 1
imageProcessingQueue.maxConcurrentOperationCount = 2
imageDecompressingQueue.maxConcurrentOperationCount = 2

There is a list of pipeline settings which you can tweak:

// Automatically decompress images in the background by default.
isDecompressionEnabled = true

// Configure which images to store in the custom disk cache.
isDataCachingForOriginalImageDataEnabled = true
isDataCachingForProcessedImagesEnabled = false

// Avoid doing any duplicated work when loading or processing images.
isDeduplicationEnabled = true

// Rate limit the requests to prevent trashing of the subsystems.
isRateLimiterEnabled = true

// Progressive decoding is an opt-in feature because it is resource intensive.
isProgressiveDecodingEnabled = false

// If the data task is terminated (either because of a failure or a
// cancellation) and the image was partially loaded, the next load will
// resume where it was left off.
isResumableDataEnabled = true

And also a few global options shared between all pipelines:

ImagePipeline.Configuration.isAnimatedImageDataEnabled = false

// Enable to start using `os_signpost` to monitor the pipeline
// performance using Instruments.
ImagePipeline.Configuration.isSignpostLoggingEnabled = false

Caching

LRU Memory Cache

Nuke's default ImagePipeline has two cache layers.

First, there is a memory cache for storing processed images which are ready for display.

// Configure cache
ImageCache.shared.costLimit = 1024 * 1024 * 100 // 100 MB
ImageCache.shared.countLimit = 100
ImageCache.shared.ttl = 120 // Invalidate image after 120 sec

// Read and write images
let request = ImageRequest(url: url)
ImageCache.shared[request] = image
let image = ImageCache.shared[request]

// Clear cache
ImageCache.shared.removeAll()

ImageCache uses the LRU algorithm – least recently used entries are removed first during the sweep.

HTTP Disk Cache

Unprocessed image data is stored with URLCache.

// Configure cache
DataLoader.sharedUrlCache.diskCapacity = 100
DataLoader.sharedUrlCache.memoryCapacity = 0

// Read and write responses
let request = ImageRequest(url: url)
let _ = DataLoader.sharedUrlCache.cachedResponse(for: request.urlRequest)
DataLoader.sharedUrlCache.removeCachedResponse(for: request.urlRequest)

// Clear cache
DataLoader.sharedUrlCache.removeAllCachedResponses()

Aggressive LRU Disk Cache

If HTTP caching is not your cup of tea, you can try using a custom LRU disk cache for fast and reliable aggressive data caching (ignores HTTP cache control). You can enable it using the pipeline configuration.

ImagePipeline {
    $0.dataCache = try! DataCache(name: "com.myapp.datacache")

    // Also consider disabling the native HTTP cache, see `DataLoader`.
}

By default, the pipeline stores only the original image data. To store the processed images enable isDataCachingForProcessedImagesEnabled and also consider disabling isDataCachingForOriginalImageDataEnabled. Every intermediate processed image will be stored in cache. So in the following scenario, there are going to be two entries in the disk cache (three if original image cache is also enabled):

let request = ImageRequest(url: url, processors: [
    ImageProcessor.Resize(size: imageView.bounds.size),
    ImageProcessor.CoreImageFilter(name: "CISepiaTone")
])

To avoid storing unwanted images, compose the processors, ImageProcessor.Composition is an easy way to do it.


Advanced Features

Image Preheating

Prefetching images in advance can dramatically improve your app's user experience.

let preheater = ImagePreheater()
preheater.startPreheating(with: urls)

// Cancels all of the preheating tasks created for the given requests.
preheater.stopPreheating(with: urls)

Keep in mind that prefetching takes up users' data and puts extra pressure on CPU and memory. To reduce the CPU and memory usage, you have an option to choose only the disk cache as a prefetching destination:

// The preheater with `.diskCache` destination will skip image data decoding
// entirely to reduce CPU and memory usage. It will still load the image data
// and store it in disk caches to be used later.
let preheater = ImagePreheater(destination: .diskCache)

On iOS, you can use prefetching APIs in combination with ImagePreheater to automate the process.

Progressive Decoding

To enable progressive image decoding set isProgressiveDecodingEnabled configuration option to true.

Progressive JPEG
let pipeline = ImagePipeline {
    $0.isProgressiveDecodingEnabled = true
}

And that's it, the pipeline will automatically do the right thing and deliver the progressive scans via progress closure as they arrive:

let imageView = UIImageView()
let task = ImagePipeline.shared.loadImage(
    with: url,
    progress: { response, _, _ in
        if let response = response {
            imageView.image = response?.image
        }
    },
    completion: { result in
        // Display the final image
    }
)

Animated Images

Nuke extends UIImage with an animatedImageData property. To enable it, set ImagePipeline.Configuration.isAnimatedImageDataEnabled to true. If you do, the pipeline will start attaching the original image data to the animated images.

There is no built-in way to render those images, but there are two extensions available: FLAnimatedImage and Gifu which are both fast and efficient.

GIF is not the most efficient format for transferring and displaying animated images. The current best practice is to use short videos instead of GIFs (e.g. MP4, WebM). There is a PoC available in the demo project which uses Nuke to load, cache and display an MP4 video.

WebP

WebP support is provided by Nuke WebP Plugin built by Ryo Kosuge. Please follow the instructions from the repo.

RxNuke

RxNuke adds RxSwift extensions for Nuke and enables some common use cases: Going from low to high resolution | Loading the first available image | Showing stale image while validating it | Load multiple images, display all at once | Auto retry on failures | And more

To get a taste of what you can do with this extension, take a look at how easy it is to load the low resolution image first and then switch to high resolution:

let pipeline = ImagePipeline.shared
Observable.concat(pipeline.loadImage(with: lowResUrl).orEmpty,
                  pipeline.loadImage(with: highResUtl).orEmpty)
    .subscribe(onNext: { imageView.image = $0 })
    .disposed(by: disposeBag)

Image Pipeline Overview

This section describes in detail what happens when you perform a call like Nuke.loadImage(with: url, into: view).

As a visual aid, use this Block Diagram.

First, Nuke synchronously checks if the image is stored in the memory cache. If the image is not in memory, Nuke calls pipeline.loadImage(with: request).

The pipeline first checks if the image or image data exists in any of its caches. It checks if the processed image exists in the memory cache, then if the processed image data exists in the custom data cache (disabled by default), then if the data cache contains the original image data. Only if there is no cached data, the pipeline will start loading the data. When the data is loaded the pipeline decodes it, applies the processors, and decompresses the image in the background.

Data Loading and Caching

A DataLoader class uses URLSession to load image data. The data is cached on disk using URLCache, which by default is initialized with memory capacity of 0 MB (we only store processed images in memory) and disk capacity of 150 MB.

See Image Caching Guide to learn more about HTTP cache.

The URLSession class natively supports the following URL schemes: data, file, ftp, http, and https.

Most developers either implement their own networking layer or use a third-party framework. Nuke supports both of these workflows. You can integrate your custom networking layer by implementing DataLoading protocol.

See Third Party Libraries guide to learn more. See also Alamofire Plugin.

Resumable Downloads

If the data task is terminated (either because of a failure or a cancelation) and the image was partially loaded, the next load will resume where it was left off. Resumable downloads require the server to support HTTP Range Requests. Nuke supports both validators (ETag and Last-Modified). The resumable downloads are enabled by default.

Memory Cache

The processed images are stored in a fast in-memory cache (ImageCache). It uses LRU (least recently used) replacement algorithm and has a limit of ~20% of available RAM. ImageCache automatically evicts images on memory warnings and removes a portion of its contents when the application enters background mode.

Deduplication

The pipeline avoids doing any duplicated work when loading images. For example, let's take these two requests:

let url = URL(string: "http://example.com/image")
pipeline.loadImage(with: ImageRequest(url: url, processors: [
    ImageProcessor.Resize(size: CGSize(width: 44, height: 44)),
    ImageProcessor.GaussianBlur(radius: 8)
]))
pipeline.loadImage(with: ImageRequest(url: url, processors: [
    ImageProcessor.Resize(size: CGSize(width: 44, height: 44))
]))

Nuke will load the data only once, resize the image once and blur it also only once. There is no duplicated work done. The work only gets cancelled when all the registered requests are, and the priority is based on the highest priority of the registered requests.

Deduplication can be disabled using isDeduplicationEnabled configuration option.

Performance

Nuke is tuned to do as little work on the main thread as possible. It uses multiple optimization techniques to achieve that: reducing the number of allocations, reducing dynamic dispatch, backing some structs by reference typed storage to reduce ARC overhead, etc.

Nuke is fully asynchronous and performs well under stress. ImagePipeline schedules its operations on dedicated queues. Each queue limits the number of concurrent tasks, respects the request priorities, and cancels the work as soon as possible. Under the extreme load, ImagePipeline will also rate limit the requests to prevent saturation of the underlying systems.

If you want to see how the system behaves, how long each operation takes, and how many are performed in parallel, enable the isSignpostLoggingEnabled option and use the os_signpost Instrument. For more information see Apple Documentation: Logging and WWDC 2018: Measuring Performance Using Logging.

Extensions

There is a variety of extensions available for Nuke:

|Name|Description| |--|--| |RxNuke|RxSwift extensions for Nuke with examples of common use cases solved by Rx| |Alamofire|Replace networking layer with Alamofire and combine the power of both frameworks| |WebP| [Community] WebP support, built by Ryo Kosuge| |Gifu|Use Gifu to load and display animated GIFs| |FLAnimatedImage|Use FLAnimatedImage to load and display animated GIFs|


Contribution

Nuke's roadmap is managed in Trello and is publically available. If you'd like to contribute, please feel free to create a PR.

Requirements

| Nuke | Swift | Xcode | Platforms | |-------------------|-------------------|--------------------|---------------------------------------------------| | Nuke 8 | Swift 5.0 | Xcode 10.2 | iOS 10.0 / watchOS 3.0 / macOS 10.12 / tvOS 10.0 | | Nuke 7.6 – 7.6.3 | Swift 4.2 – 5.0 | Xcode 10.1 – 10.2 | iOS 10.0 / watchOS 3.0 / macOS 10.12 / tvOS 10.0 | | Nuke 7.2 – 7.5.2 | Swift 4.0 – 4.2 | Xcode 9.2 – 10.1 | iOS 9.0 / watchOS 2.0 / macOS 10.10 / tvOS 9.0 |

License

Nuke is available under the MIT license. See the LICENSE file for more info.

Github

link
Stars: 3770
Help us keep the lights on

Dependencies

Used By

Total: 1

Releases

8.0 - Jul 8, 2019

Nuke 8 is the most powerful, performant, and refined release yet. It contains major advancements it some areas and brings some great new features.

Cache processed images on disk · New built-in image processors · ImagePipeline v2 · Up to 30% faster main thread performance · Result type · Improved deduplication · os_signpost integration · Refined ImageRequest API · Smart decompression · Entirely new documentation

Most of the Nuke APIs are source compatible with Nuke 7. There is also a Nuke 8 Migration Guide to help with migration.

Image Processing

#227 Cache Processed Images on Disk

ImagePipeline now supports caching of processed images on disk. To enable this feature set isDataCacheForProcessedDataEnabled to true in the pipeline configuration and provide a dataCache. You can use a built-in DataCache introduced in Nuke 7.3 or write a custom one.

Image cache can significantly improve the user experience in the apps that use heavy image processors like Gaussian Blur.

#243 New Image Processors

Nuke now ships with a bunch of built-in image processors including:

  • ImageProcessor.Resize
  • ImageProcessor.RoundedCorners
  • ImageProcessor.Circle
  • ImageProcessor.GaussianBlur
  • ImageProcessor.CoreImageFilter

There are also ImageProcessor.Anonymous to create one-off processors from closures and ImageProcessor.Composition to combine two or more processors.

#245 Simplified Processing API

Previously Nuke offered multiple different ways to add processors to the request. Now there is only one, which is also better than all of the previous versions:

let request = ImageRequest(
    url: URL(string: "http://..."),
    processors: [
        ImageProcessor.Resize(size: CGSize(width: 44, height: 44), crop: true),
        ImageProcessor.RoundedCorners(radius: 16)
    ]
)

Processors can also be set using a respective mutable processors property.

Notice that AnyImageProcessor is gone! You can simply use ImageProcessing protocol directly in places where previously you had to use a type-erased version.

#229 Smart Decompression

In the previous versions, decompression was part of the processing API and ImageDecompressor was the default processor set for each image request. This was mostly done to simplify implementation but it was confusing for the users.

In the new version, decompression runs automatically and it no longer a "processor". The new decompression is also smarter. It runs only when needed – when we know that image is still in a compressed format and wasn't decompressed by one of the image processors.

Decompression runs on a new separate imageDecompressingQueue. To disable decompression you can set a new isDecompressionEnabled pipeline configuration option to false.

#247 Avoiding Duplicated Work when Applying Processors

The pipeline avoids doing any duplicated work when loading images. Now it also avoids applying the same processors more than once. For example, let's take these two requests:

let url = URL(string: "http://example.com/image")
pipeline.loadImage(with: ImageRequest(url: url, processors: [
    ImageProcessor.Resize(size: CGSize(width: 44, height: 44)),
    ImageProcessor.GaussianBlur(radius: 8)
]))
pipeline.loadImage(with: ImageRequest(url: url, processors: [
    ImageProcessor.Resize(size: CGSize(width: 44, height: 44))
]))

Nuke will load the image data only once, resize the image once and apply the blur also only once. There is no duplicated work done at any stage. If any of the intermediate results are available in the data cache, they will be used.

ImagePipeline v2

Nuke 8 introduced a major new iteration of the ImagePipeline class. The class was introduced in Nuke 7 and it contained a lot of incidental complexity due to addition of progressive decoding and some other new features. In Nuke 8 it was rewritten to fully embrace progressive decoding. The new pipeline is smaller, simpler, easier to maintain, and more reliable.

It is also faster.

+30% Main Thread Performance

The image pipeline spends even less time on the main thread than any of the previous versions. It's up to 30% faster than Nuke 7.

#239 Load Image Data

Add a new ImagePipeline method to fetch original image data:

@discardableResult
public func loadData(with request: ImageRequest,
                     progress: ((_ completed: Int64, _ total: Int64) -> Void)? = nil,
                     completion: @escaping (Result<(data: Data, response: URLResponse?), ImagePipeline.Error>) -> Void) -> ImageTask

This method now powers ImagePreheater with destination .diskCache introduced in Nuke 7.4 (previously it was powered by a hacky internal API).

#245 Improved ImageRequest API

The rarely used options were extracted into the new ImageRequestOptions struct and the request initializer can now be used to customize all of the request parameters.

#255 filteredURL

You can now provide a filteredURL to be used as a key for caching in case the URL contains transient query parameters:

let request = ImageRequest(
    url: URL(string: "http://example.com/image.jpeg?token=123")!,
    options: ImageRequestOptions(
        filteredURL: "http://example.com/image.jpeg"
    )
)

#241 Adopt Result type

Adopt the Result type introduced in Swift 5. So instead of having a separate response and error parameters, the completion closure now has only one parameter - result.

public typealias Completion = (_ result: Result<ImageResponse, ImagePipeline.Error>) -> Void

Performance

Apart from the general performance improvements Nuke now also offers a great way to measure performance and gain visiblity into how the system behaves when loading images.

#250 Integrate os_signpost

Integrate os_signpost logs for measuring performance. To enable the logs set ImagePipeline.Configuration.isSignpostLoggingEnabled (static property) to true before accessing the shared pipeline.

With these logs, you have visibility into the image pipeline. For more information see WWDC 2018: Measuring Performance Using Logging which explains os_signpost in a great detail.

Screenshot 2019-06-01 at 10 46 52

Documentation

All the documentation for Nuke was rewritten from scratch in Nuke 8. It's now more concise, clear, and it even features some fantastic illustrations:

Screenshot 2019-06-11 at 22 31 18

The screenshots come the the reworked demo project. It gained new demos including Image Processing demo and also a way to change ImagePipeline configuration in runtime.

Misc

  • Add a cleaner way to set ImageTask priority using a new priority property – #251
  • [macOS] Implement image cost calculation for ImageCache#236
  • [watchOS] Add WKInterfaceImage support
  • Future-proof Objective-C ImageDisplaying protocol by adding nuke_ prefixes to avoid clashes in Objective-C runtime
  • Add convenience func decode(data: Data) -> Image? method with a default isFinal argument to ImageDecoding protocol – e3ca5e
  • Add convenience func process(image: Image) -> Image? method to ImageProcessing protocol
  • DataCache will now automatically re-create its root directory if it was deleted underneath it
  • Add public flush method to DataCache

8.0-rc.1 - Jun 12, 2019

Nuke 8 is the most powerful, performant, and refined release yet. It contains major advancements it some areas and brings some great new features. One of the highlights of this release is the documentation which was rewritten from the ground up.

Cache processed images on disk · New built-in image processors · ImagePipeline v2 · Up to 30% faster main thread performance · Result type · Improved deduplication · os_signpost integration · Refined ImageRequest API · Smart decompression · Entirely new documentation

Most of the Nuke APIs are source compatible with Nuke 7. There is also a Nuke 8 Migration Guide to help with migration.

Image Processing

#227 Cache Processed Images on Disk

ImagePipeline now supports caching of processed images on disk. To enable this feature set isDataCacheForProcessedDataEnabled to true in the pipeline configuration and provide a dataCache. You can use a built-in DataCache introduced in Nuke 7.3 or write a custom one.

Image cache can significantly improve the user experience in the apps that use heavy image processors like Gaussian Blur.

#243 New Image Processors

Nuke now ships with a bunch of built-in image processors including:

  • ImageProcessor.Resize
  • ImageProcessor.RoundedCorners
  • ImageProcessor.Circle
  • ImageProcessor.GaussianBlur
  • ImageProcessor.CoreImageFilter

There are also ImageProcessor.Anonymous to create one-off processors from closures and ImageProcessor.Composition to combine two or more processors.

#245 Simplified Processing API

Previously Nuke offered multiple different ways to add processors to the request. Now there is only one, which is also better than all of the previous versions:

let request = ImageRequest(
    url: URL(string: "http://..."),
    processors: [
        ImageProcessor.Resize(size: CGSize(width: 44, height: 44), crop: true),
        ImageProcessor.RoundedCorners(radius: 16)
    ]
)

Processors can also be set using a respective mutable processors property.

Notice that AnyImageProcessor is gone! You can simply use ImageProcessing protocol directly in places where previously you had to use a type-erased version.

#229 Smart Decompression

In the previous versions, decompression was part of the processing API and ImageDecompressor was the default processor set for each image request. This was mostly done to simplify implementation but it was confusing for the users.

In the new version, decompression runs automatically and it no longer a "processor". The new decompression is also smarter. It runs only when needed – when we know that image is still in a compressed format and wasn't decompressed by one of the image processors.

Decompression runs on a new separate imageDecompressingQueue. To disable decompression you can set a new isDecompressionEnabled pipeline configuration option to false.

#247 Avoiding Duplicated Work when Applying Processors

The pipeline avoids doing any duplicated work when loading images. Now it also avoids applying the same processors more than once. For example, let's take these two requests:

let url = URL(string: "http://example.com/image")
pipeline.loadImage(with: ImageRequest(url: url, processors: [
    ImageProcessor.Resize(size: CGSize(width: 44, height: 44)),
    ImageProcessor.GaussianBlur(radius: 8)
]))
pipeline.loadImage(with: ImageRequest(url: url, processors: [
    ImageProcessor.Resize(size: CGSize(width: 44, height: 44))
]))

Nuke will load the image data only once, resize the image once and apply the blur also only once. There is no duplicated work done at any stage. If any of the intermediate results are available in the data cache, they will be used.

ImagePipeline v2

Nuke 8 introduced a major new iteration of the ImagePipeline class. The class was introduced in Nuke 7 and it contained a lot of incidental complexity due to addition of progressive decoding and some other new features. In Nuke 8 it was rewritten to fully embrace progressive decoding. The new pipeline is smaller, simpler, easier to maintain, and more reliable.

It is also faster.

+30% Main Thread Performance

The image pipeline spends even less time on the main thread than any of the previous versions. It's up to 30% faster than Nuke 7.

#239 Load Image Data

Add a new ImagePipeline method to fetch original image data:

@discardableResult
public func loadData(with request: ImageRequest,
                     progress: ((_ completed: Int64, _ total: Int64) -> Void)? = nil,
                     completion: @escaping (Result<(data: Data, response: URLResponse?), ImagePipeline.Error>) -> Void) -> ImageTask

This method now powers ImagePreheater with destination .diskCache introduced in Nuke 7.4 (previously it was powered by a hacky internal API).

#245 Improved ImageRequest API

The rarely used options were extracted into the new ImageRequestOptions struct and the request initializer can now be used to customize all of the request parameters.

#255 filteredURL

You can now provide a filteredURL to be used as a key for caching in case the URL contains transient query parameters:

let request = ImageRequest(
    url: URL(string: "http://example.com/image.jpeg?token=123")!,
    options: ImageRequestOptions(
        filteredURL: "http://example.com/image.jpeg"
    )
)

#241 Adopt Result type

Adopt the Result type introduced in Swift 5. So instead of having a separate response and error parameters, the completion closure now has only one parameter - result.

public typealias Completion = (_ result: Result<ImageResponse, ImagePipeline.Error>) -> Void

Performance

Apart from the general performance improvements Nuke now also offers a great way to measure performance and gain visiblity into how the system behaves when loading images.

#250 Integrate os_signpost

Integrate os_signpost logs for measuring performance. To enable the logs set ImagePipeline.Configuration.isSignpostLoggingEnabled (static property) to true before accessing the shared pipeline.

With these logs, you have visibility into the image pipeline. For more information see WWDC 2018: Measuring Performance Using Logging which explains os_signpost in a great detail.

Screenshot 2019-06-01 at 10 46 52

Documentation

All the documentation for Nuke was rewritten from scratch in Nuke 8. It's now more concise, clear, and it even features some fantastic illustrations:

Screenshot 2019-06-11 at 22 31 18

The screenshots come the the reworked demo project. It gained new demos including Image Processing demo and also a way to change ImagePipeline configuration in runtime.

Misc

  • Add a cleaner way to set ImageTask priority using a new priority property – #251
  • [macOS] Implement image cost calculation for ImageCache#236
  • [watchOS] Add WKInterfaceImage support
  • Future-proof Objective-C ImageDisplaying protocol by adding nuke_ prefixes to avoid clashes in Objective-C runtime
  • Add convenience func decode(data: Data) -> Image? method with a default isFinal argument to ImageDecoding protocol – e3ca5e
  • Add convenience func process(image: Image) -> Image? method to ImageProcessing protocol
  • DataCache will now automatically re-create its root directory if it was deleted underneath it
  • Add public flush method to DataCache

8.0-beta.4 - Jun 7, 2019

A small bump to address the feedback from Nuke 8.0-beta.3.

  • Improve debug descriptions for processors, adopt reverse DNS notation for cache keys
  • Revert the changes made in #254 Improve image view extensions. There is going to be no major changes in the primary Nuke.loadImage(:) API in Nuke 8. The extensions are going to be revisited when adding SwiftUI and Combine support.
  • Tune the performance, now we can safely say there is a +25% improvement in some of the common use cases on the main thread.
  • [Documentation] Update some of the documentation
  • [Demo] Add a Settings screen to the demo project to allow change ImagePipeline configuration in runtime

8.0-beta.3 - Jun 2, 2019

#257 Image Processing Improvements

  • Refactor the existing processors
  • Add new ImageProcessor.Crop, previously cropping was an option in ImageProcessor.Resize and there was a known defect when cropping images smaller than the target size which is fixed in the new implementation
  • Make ImageProcessor.Resize and ImageProcessor.Crop available on macOS
  • Add CustomStringConvertible implementations for each image processor
  • Add documentation for image processors

#259 Revert Addition of ImageTaskDelegate

  • ImageTaskDelegate was a new API introduced in Nuke 8.0-beta.1 to improve the pipeline performance. After some extensive profiling it was clear that this change wasn't truly worth the increased complexity and the changes in the established public API, thus this change was reverted.

8.0-beta.2 - Jun 1, 2019

There quite a few important changes in this update. Probably the most important one is the addition of Deprecated.swift file which eases the transition from Nuke 7 to Nuke 8 which are now almost completely source compatible.

Naturally, this beta includes everything from Nuke 8.0-beta.1.

#250 Integrate os_signpost for profiling

Integrate os_signpost logs for measuring performance. To enable the logs set ImagePipeline.Configuration.isSignpostLoggingEnabled (static property) to true before accessing the shared pipeline.

With these logs, you have visibility into the image pipeline. For more information see WWDC 2018: Measuring Performance Using Logging which explains os_signpost in a great detail.

Screenshot 2019-06-01 at 10 46 52

#254 Improve Image Loading Extensions

  • Add imageView.nk.setImage(with:) family of methods as a replacement for Nuke.loadImage(with:into:). The latter wasn't grammatically correct, that's the first. setImage(with:) methods are also more conventional. So now in order to load an image and display it in a view you would simply do this:
imageView.nk.setImage(with: URL(string: "http://example.com/image.jpeg")!)
  • Future-proof Objective-C ImageDisplaying protocol by adding nuke_ prefixes to avoid clashes in Objective-C runtime
  • Add WKInterfaceImage support

Other Changes

  • Enable processing deduplication by default – #252
  • Add filterURL options to ImageRequestOptions to filter unwanted query parameters when generating cache keys - #255
  • Add Deprecated.swift file to ease transition from Nuke 7 to Nuke 8 - #253
  • Implement image cost calculation in ImageCache for macOS – #236
  • Add a cleaner way to set ImageTask priority using a new priority property – #251
  • Add convenience func decode(data: Data) method with a default isFinal argument to ImageDecoding protocol – e3ca5e
  • DataCache will now automatically re-create its root directory if it was deleted underneath it