Skip to content

Releases: kean/Nuke

Nuke 13.0.1

29 Mar 17:44

Choose a tag to compare

  • Fix ThumbnailOptions double-applying EXIF orientation when createThumbnailWithTransform is enabled – #870
  • Fix Xcode 26.0 compatibility – #871

Nuke 13.0

22 Mar 23:58

Choose a tag to compare

Nuke 13 achieves full Data Race Safety by migrating all pipeline work to Swift Concurrency, replacing DispatchQueue and OperationQueue with a @globalActor-based synchronization model. It also ships over 10 new APIs, including progressive preview policies, a willLoadData auth hook, memory size limits, and type-safe ImageRequest options.

Requirements

  • Minimum supported Xcode version: 26.0.
  • Minimum required platforms: iOS 15.0, watchOS 8.0, macOS 12.0, tvOS 13.0, visionOS 1.0

Quality

The test suite was rewritten in Swift Testing with Swift 6 mode enabled and significantly expanded. Despite the additional tests, the suite is 3x faster thanks to the parallelization.

Version Source lines Tests Test lines Coverage Test Time
Nuke 13.0 4,669 768 8,509 96.0% 1.3s
Nuke 12.9 4,589 496 6,167 92.4% 3.5s

Concurrency & Data Race Safety

The codebase is migrated to Swift Concurrency and supports Data Race Safety, with some minor exceptions that will be addressed in the future iterations.

  • Replace the internal serial DispatchQueue with a @globalActor (ImagePipelineActor) for pipeline synchronization, making thread-safety compiler-enforced. The actor is public so that custom ImagePipeline.Delegate implementations can use it when needed to reduce thread hops
  • Replace OperationQueue-based scheduling with a custom TaskQueue synchronized on ImagePipelineActor. Background operations like image processing and decoding now run on the default Swift Concurrency executors, eliminating unnecessary thread hops. The entire pipeline is now a good Swift Concurrency citizen
  • Add typed throws (throws(ImagePipeline.Error)) to ImageTask.image, ImageTask.response, ImagePipeline.image(for:), and ImagePipeline.data(for:). Add ImagePipeline.Error.cancelled case. Cancellation now throws this instead of CancellationError.
  • Change userInfo type from [UserInfoKey: Any] to [UserInfoKey: any Sendable] in both ImageRequest and ImageContainer
  • Add @MainActor @Sendable to completion-based loadImage/loadData closure parameters
  • Add @MainActor @Sendable to progress and completion closures in NukeExtensions loadImage functions
  • Add @MainActor @Sendable to all callback closures in NukeUI: FetchImage.onStart/onCompletion, LazyImage.onStart/onCompletion modifiers, LazyImageView.onStart/onPreview/onProgress/onSuccess/onFailure/onCompletion
  • Eliminate an actor hop during ImageTask startup, reducing per-request overhead
  • Synchronize ResumableDataStorage on ImagePipelineActor, replacing NSLock with actor isolation and removing @unchecked Sendable.
  • Convert unit tests to Swift Testing and enable Swift 6 mode for all tests

New Features

  • Add ImagePipeline.PreviewPolicy (.incremental, .thumbnail, .disabled) to control how progressive previews are generated per-request
  • Add ImagePipelineDelegate.previewPolicy(for:pipeline:) for customizing the policy dynamically. Default policy: .incremental for progressive JPEGs and GIFs, .disabled for everything else (baseline JPEGs, PNGs, etc.), restoring the original behavior before CGImageSourceCreateIncremental was adopted
  • Add ImagePipeline.Delegate.willLoadData(for:urlRequest:pipeline:), an async, throwing hook that intercepts the URLRequest just before data loading begins. Use it to inject auth tokens, sign requests, or perform any async pre-flight work. Throw to cancel with a meaningful error (e.g., when a token refresh fails). Default implementation returns the request unchanged — #774
  • Add ImageRequest.init(id:image:) that accepts an async closure returning an ImageContainer directly. Use it to process images already in memory or to integrate with systems that provide pre-decoded images (e.g., Photos framework). The image skips data decoding entirely and is loaded in TaskFetchOriginalImage#823
  • Add type-safe imageID, scale, and thumbnail properties to ImageRequest, replacing the previous userInfo dictionary-based approach. The new properties are more ergonomic and improve performance by eliminating dictionary lookups and Any boxing. The userInfo[.imageIdKey], userInfo[.scaleKey], and userInfo[.thumbnailKey] keys are deprecated. The new imageID property replaces imageId to follow idiomatic Swift naming (uppercase "ID") and is now also writable – #772
  • Add ImagePipeline.Configuration.progressiveDecodingInterval (default: 0.5s) to throttle progressive decoding attempts when data arrives faster than the interval
  • Add ImagePipeline.Configuration.maximumResponseDataSize — downloads that exceed this limit are automatically cancelled. The default limit is based on the device's physical memory. Set to nil to disable — #738
  • Add ImagePipeline.Configuration.maximumDecodedImageSize — images whose decoded bitmap would exceed this limit are automatically downscaled during decoding. The default limit is calculated dynamically based on the device's physical memory. Set to nil to disable
  • Add DataCache.isSweepEnabled (true by default). Set it to false in targets that share a cache with the main app (e.g. a Notification Service Extension) so that only the main app enforces size limits via LRU sweeps
  • Add AssetType.ico with magic-byte detection for ICO (Windows icon) images
  • Add ImageTask.Event.started
  • Mark all public enums as @frozen (except error enums and empty namespaces)

Performance

  • Rewrite ImageProcessors.GaussianBlur to use Accelerate (vImageBoxConvolve) instead of Core Image, fixing gray border artifacts and improving performance ~5.8x — #308
  • Optimize data downloading by pre-allocating the buffer using the expected content size from the HTTP response, reducing memory reallocations during image downloads (this only applies when progressive decoding is on) — #738
  • Update ImageCache.defaultCostLimit to 15% of physical memory and a hard cap of 768 MB (previously 20% capped at 512 MB). The cache uses a custom LRU policy that enforces limits precisely, so 15% is effectively more generous than the previous capped value on modern devices – #838
  • The storage cost limit of ResumableDataStorage is now dynamic and varies depending on the available RAM.
  • Add consuming to LazyImage builder methods (processors, priority, pipeline, onStart, onDisappear, onCompletion) and ImageContainer.map(_:)

API Changes

  • Rename ImagePipelineDelegate to ImagePipeline.Delegate. A deprecated ImagePipelineDelegate typealias is provided for backward compatibility
  • Refactor ImageDecoders.Default to fully delegate incremental decoding to Image I/O via CGImageSourceCreateIncremental
  • Remove queue parameter from completion-based loadImage/loadData methods — callbacks now always run on the main queue
  • Remove ImageTask.Event.cancelled in favor of .finished(.failure(.cancelled)) — cancellation is now uniformly represented as a failure result
  • Remove ImageRequest.init(id:dataPublisher:) and internal TaskFetchWithPublisher. Use ImageRequest.init(id:data:) (async closure) instead — it is now handled directly by TaskFetchOriginalData
  • Remove soft-deprecated per-event ImagePipelineDelegate methods (imageTaskDidStart, didUpdateProgress, didReceivePreview, imageTaskDidCancel, didCompleteWithResult). Use imageTask(_:didReceiveEvent:pipeline:) instead
  • Remove previously deprecated APIs: DataCache.isCompressionEnabled, ImageProcessors.Resize.ContentMode typealias, AsyncImageTask typealias, ImagePipeline.Configuration.callbackQueue, ImagePipeline.Configuration.dataCachingQueue, ImagePipeline.loadData(with: URL), and ImagePipeline.data(for: URL)
  • Soft-deprecate the userInfo parameter in ImageRequest initializers in favor of dedicated type-safe properties

Bug Fixes

  • Fix progressive JPEGs with large EXIF headers not producing previews — CGImageSourceCreateIncremental fails to recognize these files until fully downloaded. The decoder now falls back to generating a thumbnail from a non-incremental source. The issue was raised by and the initial fix provided by @theop-luma in #835
  • Fix thumbnail requests re-downloading original image data when it is already stored in the disk cache — #837
  • Fix ImageTask.state remaining .running after completion when using the completion-based loadImage API
  • Fix ImageDecoders.Video.decode(_:) returning an empty image instead of a video thumbnail — #811
  • Fix VideoPlayerView accumulating duplicate AVPlayerItemDidPlayToEndTime observers on each play()/reset() cycle, causing onVideoFinished to fire multiple times — #818

New Contributors

Nuke 13.0 (Beta 2)

16 Mar 01:06

Choose a tag to compare

Nuke 13.0 (Beta 2) Pre-release
Pre-release

Nuke 13 achieves Data Race Safety by migrating pipeline work to Swift Concurrency, replacing DispatchQueue and OperationQueue with a @globalActor-based synchronization model. It also ships over 10 new APIs, including progressive preview policies, a willLoadData auth hook, memory size limits, and type-safe ImageRequest properties.

Requirements

  • Minimum supported Xcode version: 26.0.
  • Minimum required platforms: iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15.

Quality

  • Convert unit tests to Swift Testing and enable Swift 6 mode for all tests and expand test coverage
Version Source lines Tests Test lines Coverage
Nuke 13.0 4,669 768 8,509 96.0%
Nuke 12.9 4,589 496 6,167 92.4%

Concurrency & Data Race Safety

  • Replace the internal serial DispatchQueue with a @globalActor (ImagePipelineActor) for pipeline synchronization, making thread-safety compiler-enforced. The actor is public so that custom ImagePipeline.Delegate implementations can use it when needed to reduce thread hops
  • Replace OperationQueue-based scheduling with a custom TaskQueue synchronized on ImagePipelineActor. Background operations like image processing and decoding now run on the default Swift Concurrency executors, eliminating unnecessary thread hops. The entire pipeline is now a good Swift Concurrency citizen
  • Replace callback-based DataLoading protocol with async/await: loadData(with:) now returns (AsyncThrowingStream<Data, Error>, URLResponse). Remove Cancellable protocol
  • Add typed throws (throws(ImagePipeline.Error)) to ImageTask.image, ImageTask.response, ImagePipeline.image(for:), and ImagePipeline.data(for:). Add ImagePipeline.Error.cancelled case. Cancellation now throws this instead of CancellationError.
  • Change userInfo type from [UserInfoKey: Any] to [UserInfoKey: any Sendable] in both ImageRequest and ImageContainer
  • Add @MainActor @Sendable to completion-based loadImage/loadData closure parameters
  • Add @MainActor @Sendable to progress and completion closures in NukeExtensions loadImage functions
  • Add @MainActor @Sendable to all callback closures in NukeUI: FetchImage.onStart/onCompletion, LazyImage.onStart/onCompletion modifiers, LazyImageView.onStart/onPreview/onProgress/onSuccess/onFailure/onCompletion
  • Eliminate an actor hop during ImageTask startup, reducing per-request overhead
  • Synchronize ResumableDataStorage on ImagePipelineActor, replacing NSLock with actor isolation and removing @unchecked Sendable.

New Features

  • Add ImagePipeline.PreviewPolicy (.incremental, .thumbnail, .disabled) to control how progressive previews are generated per-request
  • Add ImagePipelineDelegate.previewPolicy(for:pipeline:) for customizing the policy dynamically. Default policy: .incremental for progressive JPEGs and GIFs, .disabled for everything else (baseline JPEGs, PNGs, etc.), restoring the original behavior before CGImageSourceCreateIncremental was adopted
  • Add ImagePipeline.Delegate.willLoadData(for:urlRequest:pipeline:), an async, throwing hook that intercepts the URLRequest just before data loading begins. Use it to inject auth tokens, sign requests, or perform any async pre-flight work. Throw to cancel with a meaningful error (e.g., when a token refresh fails). Default implementation returns the request unchanged — #774
  • Add ImageRequest.init(id:image:) that accepts an async closure returning an ImageContainer directly. Use it to process images already in memory or to integrate with systems that provide pre-decoded images (e.g., Photos framework). The image skips data decoding entirely and is loaded in TaskFetchOriginalImage#823
  • Add type-safe imageID, scale, and thumbnail properties to ImageRequest, replacing the previous userInfo dictionary-based approach. The new properties are more ergonomic and improve performance by eliminating dictionary lookups and Any boxing. The userInfo[.imageIdKey], userInfo[.scaleKey], and userInfo[.thumbnailKey] keys are deprecated. The new imageID property replaces imageId to follow idiomatic Swift naming (uppercase "ID") and is now also writable – #772
  • Add ImagePipeline.Configuration.progressiveDecodingInterval (default: 0.5s) to throttle progressive decoding attempts when data arrives faster than the interval
  • Add ImagePipeline.Configuration.maximumResponseDataSize — downloads that exceed this limit are automatically cancelled. The default limit is based on the device's physical memory. Set to nil to disable — #738
  • Add ImagePipeline.Configuration.maximumDecodedImageSize — images whose decoded bitmap would exceed this limit are automatically downscaled during decoding. The default limit is calculated dynamically based on the device's physical memory. Set to nil to disable
  • Add DataCache.isSweepEnabled (true by default). Set it to false in targets that share a cache with the main app (e.g. a Notification Service Extension) so that only the main app enforces size limits via LRU sweeps
  • Add AssetType.ico with magic-byte detection for ICO (Windows icon) images
  • Add ImageTask.Event.started
  • Mark all public enums as @frozen (except error enums and empty namespaces)

Performance

  • Rewrite ImageProcessors.GaussianBlur to use Accelerate (vImageBoxConvolve) instead of Core Image, fixing gray border artifacts and improving performance ~5.8x — #308
  • Optimize data downloading by pre-allocating the buffer using the expected content size from the HTTP response, reducing memory reallocations during image downloads (this only applies when progressive decoding is on) — #738
  • Update ImageCache.defaultCostLimit to 15% of physical memory with no hard cap (previously 20% capped at 512 MB). The cache uses a custom LRU policy that enforces limits precisely, so 15% is effectively more generous than the previous capped value on modern devices – #838
  • The storage cost limit of ResumableDataStorage is now dynamic and varies depending on the available RAM.
  • Add consuming to LazyImage builder methods (processors, priority, pipeline, onStart, onDisappear, onCompletion) and ImageContainer.map(_:)

API Changes

  • Rename ImagePipelineDelegate to ImagePipeline.Delegate. A deprecated ImagePipelineDelegate typealias is provided for backward compatibility
  • Refactor ImageDecoders.Default to fully delegate incremental decoding to Image I/O via CGImageSourceCreateIncremental
  • Remove queue parameter from completion-based loadImage/loadData methods — callbacks now always run on the main queue
  • Remove ImageTask.Event.cancelled in favor of .finished(.failure(.cancelled)) — cancellation is now uniformly represented as a failure result
  • Remove ImageRequest.init(id:dataPublisher:) and internal TaskFetchWithPublisher. Use ImageRequest.init(id:data:) (async closure) instead — it is now handled directly by TaskFetchOriginalData
  • Remove soft-deprecated per-event ImagePipelineDelegate methods (imageTaskDidStart, didUpdateProgress, didReceivePreview, imageTaskDidCancel, didCompleteWithResult). Use imageTask(_:didReceiveEvent:pipeline:) instead
  • Remove previously deprecated APIs: DataCache.isCompressionEnabled, ImageProcessors.Resize.ContentMode typealias, AsyncImageTask typealias, ImagePipeline.Configuration.callbackQueue, ImagePipeline.Configuration.dataCachingQueue, ImagePipeline.loadData(with: URL), and ImagePipeline.data(for: URL)
  • Soft-deprecate the userInfo parameter in ImageRequest initializers in favor of dedicated type-safe properties

Bug Fixes

  • Fix progressive JPEGs with large EXIF headers not producing previews — CGImageSourceCreateIncremental fails to recognize these files until fully downloaded. The decoder now falls back to generating a thumbnail from a non-incremental source. The issue was raised by and the initial fix provided by @theop-luma in #835
  • Fix thumbnail requests re-downloading original image data when it is already stored in the disk cache — #837

Nuke 12.9

23 Feb 00:38

Choose a tag to compare

What's Changed

  • Enable Swift 6 and fix remaining concurrency warnings
  • Optimize ImageTask AsyncStreamAPIs and remove the Combine dependency. It now essentially has no overhead.
  • Updating misleading SVG support by @realmtai in #839
  • Fix deprecation warning typo by @cameronmcefee in #861
  • Mark DataLoading closures as @Sendable by @plu in #862
  • .storeAll now stores processed images for locals too, as it should be by @HyperfocusDisordered in #810
  • Add .heic support to AssetType/init so it can detect it based on the input Data
  • Remove some @unchecked markers from Sendable types for better Data Race Safety checking
  • Fix an issue with DateCache not touching .contentAccessDate when accessing files

New Contributors

Nuke 13.0 (Beta 1)

27 Oct 16:17

Choose a tag to compare

Nuke 13.0 (Beta 1) Pre-release
Pre-release

Nuke 13 is an incremental release that takes more advantage of Swift Concurrency and Data Race Safety by introducing a global ImagePipelineActor on which its subsystems are all synchronized. It ensures thread-safety, removes 20 usages of @unchecked Sendable, and makes the framework even leaner with ~5% less code in the main module. The future versions will completely switch to Swift Concurrency, but it will require a couple of more iterations.

Requirement: Xcode 16.

  • Increase deployment targets to iOS 14, tvOS 14, macOS 11, watchOS 7
  • Add global actor ImagePipelineActor. ImagePipeline, ImageTask, ImagePrefetcher, and some other internal types are synchronized on the new actor.
  • Soft-deprecate closure-based ImagePipeline APIs
  • Soft-deprecate ImagePipeline Combine extension and move them to NukeExtensions
  • Remove ImagePipeline.Configuration.callbackQueue
  • Remove queue parameter from ImagePipeline loadImage and loadData method. The callbacks are now isolated to the @MainActor and are @Sendable.
  • Add ImageTask.isCancelling to make it easier to transition to Nuke 13 in case you were using ImageTask.state for checking for cancellation invoked by the client (this might change in the upcoming betas)
  • Rename ImagePipelineDelegate to ImagePipeline.Delegate
  • Remove ImagePipeline.Delegate imageTaskDidStart and other methods soft-deprecated in Nuke 12.7.
  • Remove ImageRequest initializers accepting dataPublisher (Combine) – use Swift Concurrency instead
  • Update DataLoading protocol to use Swift Concurrency instead of closures
  • Deprecate FetchImage Combine support

Nuke 12.8

13 Jul 14:47

Choose a tag to compare

What's Changed

  • Add Swift 6 compatibility
  • Add @MainActor annotation to ImageLoadingOptions.shared
  • Fix image scale and orientation issue in thumbnail creation by @prabhuamol in #793
  • Deprecate ImagePipeline.Configuration.callbackQueue – this feature will be removed in Nuke 13
  • ImagePrefetcher.didComplete closure is now annotated with @MainActor @Sendable
  • Drop Xcode 14 support

New Contributors

Nuke 12.8 (Beta 1)

07 Jul 19:22

Choose a tag to compare

Nuke 12.8 (Beta 1) Pre-release
Pre-release
  • Deprecate ImagePipeline.Configuration.callbackQueue – this feature will be removed in Nuke 13
  • ImagePrefetcher.didComplete closure is now annotated with @MainActor @Sendable
  • Add @MainActor annotation to ImageLoadingOptions.shared
  • Drop Xcode 14 support

Nuke 12.7.3

20 Jun 01:38

Choose a tag to compare

  • Fix “unrecognized selector sent to instance” crash in NukeExtensions in a cross-dissolve transition on iOS < 17 by @ejensen in #792

Nuke 12.7.2

08 Jun 19:46

Choose a tag to compare

  • Fix #789, an issue with ImageProcessors.Resize failing to resize images with packed pixel formats

Nuke 12.7.1

31 May 03:31

Choose a tag to compare

  • Fix fade transition in some scenarios by @ejensen in #786
  • Remove taskDescription from network tasks by @ejensen in #785
  • Temporarily revert the change introduced in v12.6 that would skip decompression for some image types – more info in #788