Releases: kean/Nuke
Nuke 13.0.6
Nuke 13.0.5
- Optimize
ImageCachereads, writes, and concurrent access - Optimize
LazyImageView,LazyImage, andFetchImageperformance - Fix an issue with a deprecated closure-based
ImagePipelineAPI sometimes invoked after cancellation
Nuke 13.0.4
- Add missing
@ImagePipelineActorisolation to the newwillLoadDatamethod inImagePipeline.Delegateto avoid thread hops - Minor performance improvements
Nuke 13.0.3
- Minor performance optimizations
- Remove a few
@unchecked Sendableannotations - Deprecate
ImagePipeline.Configuration.maximumDecodedImageSizeandImageDecodingContext.maximumDecodedImageSize. The automatic downscaling implementation has been removed; setting these values has no effect. UseImageRequest.ThumbnailOptionsto control decoded image size on a per-request basis
Nuke 13.0.2
- Fix
ImageDecoders.Defaultdouble-applying EXIF orientation when downscaling images that exceedmaximumDecodedImageSize - Fix the default
maximumDecodedImageSizebeing applied too aggressively
Nuke 13.0.1
Nuke 13.0
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
DispatchQueuewith a@globalActor(ImagePipelineActor) for pipeline synchronization, making thread-safety compiler-enforced. The actor is public so that customImagePipeline.Delegateimplementations can use it when needed to reduce thread hops - Replace
OperationQueue-based scheduling with a customTaskQueuesynchronized onImagePipelineActor. 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)) toImageTask.image,ImageTask.response,ImagePipeline.image(for:), andImagePipeline.data(for:). AddImagePipeline.Error.cancelledcase. Cancellation now throws this instead ofCancellationError. - Change
userInfotype from[UserInfoKey: Any]to[UserInfoKey: any Sendable]in bothImageRequestandImageContainer - Add
@MainActor @Sendableto completion-basedloadImage/loadDataclosure parameters - Add
@MainActor @Sendabletoprogressandcompletionclosures inNukeExtensionsloadImagefunctions - Add
@MainActor @Sendableto all callback closures inNukeUI:FetchImage.onStart/onCompletion,LazyImage.onStart/onCompletionmodifiers,LazyImageView.onStart/onPreview/onProgress/onSuccess/onFailure/onCompletion - Eliminate an actor hop during
ImageTaskstartup, reducing per-request overhead - Synchronize
ResumableDataStorageonImagePipelineActor, replacingNSLockwith 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:.incrementalfor progressive JPEGs and GIFs,.disabledfor everything else (baseline JPEGs, PNGs, etc.), restoring the original behavior beforeCGImageSourceCreateIncrementalwas adopted - Add
ImagePipeline.Delegate.willLoadData(for:urlRequest:pipeline:), an async, throwing hook that intercepts theURLRequestjust 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 anImageContainerdirectly. 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 inTaskFetchOriginalImageβ #823 - Add type-safe
imageID,scale, andthumbnailproperties toImageRequest, replacing the previoususerInfodictionary-based approach. The new properties are more ergonomic and improve performance by eliminating dictionary lookups andAnyboxing. TheuserInfo[.imageIdKey],userInfo[.scaleKey], anduserInfo[.thumbnailKey]keys are deprecated. The newimageIDproperty replacesimageIdto 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 tonilto 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 tonilto disable - Add
DataCache.isSweepEnabled(trueby default). Set it tofalsein 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.icowith 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.GaussianBlurto 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.defaultCostLimitto 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
ResumableDataStorageis now dynamic and varies depending on the available RAM. - Add
consumingtoLazyImagebuilder methods (processors,priority,pipeline,onStart,onDisappear,onCompletion) andImageContainer.map(_:)
API Changes
- Rename
ImagePipelineDelegatetoImagePipeline.Delegate. A deprecatedImagePipelineDelegatetypealias is provided for backward compatibility - Refactor
ImageDecoders.Defaultto fully delegate incremental decoding to Image I/O viaCGImageSourceCreateIncremental - Remove
queueparameter from completion-basedloadImage/loadDatamethods β callbacks now always run on the main queue - Remove
ImageTask.Event.cancelledin favor of.finished(.failure(.cancelled))β cancellation is now uniformly represented as a failure result - Remove
ImageRequest.init(id:dataPublisher:)and internalTaskFetchWithPublisher. UseImageRequest.init(id:data:)(async closure) instead β it is now handled directly byTaskFetchOriginalData - Remove soft-deprecated per-event
ImagePipelineDelegatemethods (imageTaskDidStart,didUpdateProgress,didReceivePreview,imageTaskDidCancel,didCompleteWithResult). UseimageTask(_:didReceiveEvent:pipeline:)instead - Remove previously deprecated APIs:
DataCache.isCompressionEnabled,ImageProcessors.Resize.ContentModetypealias,AsyncImageTasktypealias,ImagePipeline.Configuration.callbackQueue,ImagePipeline.Configuration.dataCachingQueue,ImagePipeline.loadData(with: URL), andImagePipeline.data(for: URL) - Soft-deprecate the
userInfoparameter inImageRequestinitializers in favor of dedicated type-safe properties
Bug Fixes
- Fix progressive JPEGs with large EXIF headers not producing previews β
CGImageSourceCreateIncrementalfails 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.stateremaining.runningafter completion when using the completion-basedloadImageAPI - Fix
ImageDecoders.Video.decode(_:)returning an empty image instead of a video thumbnail β #811 - Fix
VideoPlayerViewaccumulating duplicateAVPlayerItemDidPlayToEndTimeobservers on eachplay()/reset()cycle, causingonVideoFinishedto fire multiple times β #818
New Contributors
- @theop-luma made their first contribution in #835
Nuke 13.0 (Beta 2)
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
DispatchQueuewith a@globalActor(ImagePipelineActor) for pipeline synchronization, making thread-safety compiler-enforced. The actor is public so that customImagePipeline.Delegateimplementations can use it when needed to reduce thread hops - Replace
OperationQueue-based scheduling with a customTaskQueuesynchronized onImagePipelineActor. 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
DataLoadingprotocol with async/await:loadData(with:)now returns(AsyncThrowingStream<Data, Error>, URLResponse). RemoveCancellableprotocol - Add typed throws (
throws(ImagePipeline.Error)) toImageTask.image,ImageTask.response,ImagePipeline.image(for:), andImagePipeline.data(for:). AddImagePipeline.Error.cancelledcase. Cancellation now throws this instead ofCancellationError. - Change
userInfotype from[UserInfoKey: Any]to[UserInfoKey: any Sendable]in bothImageRequestandImageContainer - Add
@MainActor @Sendableto completion-basedloadImage/loadDataclosure parameters - Add
@MainActor @Sendabletoprogressandcompletionclosures inNukeExtensionsloadImagefunctions - Add
@MainActor @Sendableto all callback closures inNukeUI:FetchImage.onStart/onCompletion,LazyImage.onStart/onCompletionmodifiers,LazyImageView.onStart/onPreview/onProgress/onSuccess/onFailure/onCompletion - Eliminate an actor hop during
ImageTaskstartup, reducing per-request overhead - Synchronize
ResumableDataStorageonImagePipelineActor, replacingNSLockwith 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:.incrementalfor progressive JPEGs and GIFs,.disabledfor everything else (baseline JPEGs, PNGs, etc.), restoring the original behavior beforeCGImageSourceCreateIncrementalwas adopted - Add
ImagePipeline.Delegate.willLoadData(for:urlRequest:pipeline:), an async, throwing hook that intercepts theURLRequestjust 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 anImageContainerdirectly. 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 inTaskFetchOriginalImageβ #823 - Add type-safe
imageID,scale, andthumbnailproperties toImageRequest, replacing the previoususerInfodictionary-based approach. The new properties are more ergonomic and improve performance by eliminating dictionary lookups andAnyboxing. TheuserInfo[.imageIdKey],userInfo[.scaleKey], anduserInfo[.thumbnailKey]keys are deprecated. The newimageIDproperty replacesimageIdto 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 tonilto 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 tonilto disable - Add
DataCache.isSweepEnabled(trueby default). Set it tofalsein 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.icowith 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.GaussianBlurto 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.defaultCostLimitto 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
ResumableDataStorageis now dynamic and varies depending on the available RAM. - Add
consumingtoLazyImagebuilder methods (processors,priority,pipeline,onStart,onDisappear,onCompletion) andImageContainer.map(_:)
API Changes
- Rename
ImagePipelineDelegatetoImagePipeline.Delegate. A deprecatedImagePipelineDelegatetypealias is provided for backward compatibility - Refactor
ImageDecoders.Defaultto fully delegate incremental decoding to Image I/O viaCGImageSourceCreateIncremental - Remove
queueparameter from completion-basedloadImage/loadDatamethods β callbacks now always run on the main queue - Remove
ImageTask.Event.cancelledin favor of.finished(.failure(.cancelled))β cancellation is now uniformly represented as a failure result - Remove
ImageRequest.init(id:dataPublisher:)and internalTaskFetchWithPublisher. UseImageRequest.init(id:data:)(async closure) instead β it is now handled directly byTaskFetchOriginalData - Remove soft-deprecated per-event
ImagePipelineDelegatemethods (imageTaskDidStart,didUpdateProgress,didReceivePreview,imageTaskDidCancel,didCompleteWithResult). UseimageTask(_:didReceiveEvent:pipeline:)instead - Remove previously deprecated APIs:
DataCache.isCompressionEnabled,ImageProcessors.Resize.ContentModetypealias,AsyncImageTasktypealias,ImagePipeline.Configuration.callbackQueue,ImagePipeline.Configuration.dataCachingQueue,ImagePipeline.loadData(with: URL), andImagePipeline.data(for: URL) - Soft-deprecate the
userInfoparameter inImageRequestinitializers in favor of dedicated type-safe properties
Bug Fixes
- Fix progressive JPEGs with large EXIF headers not producing previews β
CGImageSourceCreateIncrementalfails 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
What's Changed
- Enable Swift 6 and fix remaining concurrency warnings
- Optimize
ImageTaskAsyncStreamAPIs 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
DataLoadingclosures as@Sendableby @plu in #862 .storeAllnow stores processed images for locals too, as it should be by @HyperfocusDisordered in #810- Add
.heicsupport toAssetType/initso it can detect it based on the inputData - Remove some
@uncheckedmarkers fromSendabletypes for better Data Race Safety checking - Fix an issue with
DateCachenot touching.contentAccessDatewhen accessing files
New Contributors
- @realmtai made their first contribution in #839
- @cameronmcefee made their first contribution in #861
- @plu made their first contribution in #862
- @HyperfocusDisordered made their first contribution in #810
Nuke 13.0 (Beta 1)
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
ImagePipelineAPIs - Soft-deprecate
ImagePipelineCombine extension and move them toNukeExtensions - Remove
ImagePipeline.Configuration.callbackQueue - Remove
queueparameter fromImagePipelineloadImageandloadDatamethod. The callbacks are now isolated to the@MainActorand are@Sendable. - Add
ImageTask.isCancellingto make it easier to transition to Nuke 13 in case you were usingImageTask.statefor checking for cancellation invoked by the client (this might change in the upcoming betas) - Rename
ImagePipelineDelegatetoImagePipeline.Delegate - Remove
ImagePipeline.DelegateimageTaskDidStartand other methods soft-deprecated in Nuke 12.7. - Remove
ImageRequestinitializers acceptingdataPublisher(Combine) β use Swift Concurrency instead - Update
DataLoadingprotocol to use Swift Concurrency instead of closures - Deprecate
FetchImageCombine support