Repository navigation
Releases: kean/Nuke
Release list
Nuke 13.2.0
What's Changed
- Add
AssetType/utTypeandAssetType/init(_:)for bridging to and fromUTType - Add
AssetType/bmp,AssetType/tiff,AssetType/jpeg2000, andAssetType/jxl. Image I/O decodes these formats, butAssetType/init(_:)didn't recognize them, soImageContainer/typewasnil - Fix
AssetType/init(_:)detecting GIF from the first three bytes instead of the completeGIF87a/GIF89asignature, which kept the original data in the memory cache for the files that merely started withGIF - Fix
AssetType/webp,AssetType/mp4,AssetType/m4v, andAssetType/movusing the identifiers that the system doesn't declare, so they didn't bridge to aUTType - Fix MP4 files with the standard
mp42major brand being detected asAssetType/m4v - Detect the ISO base media types from the major brand in the
ftypbox, which adds the remaining MP4, M4V, and HEIC brands - Fix progressive image downloads not completing when
isResetEnabledistrueonLazyImageViewby @nathantannar4 in #884 - Fix
ImagePipeline.Cache.storeCachedImage(_:for:caches:)storing image previews in the disk cache, so a progressive scan passed to it was later delivered as a completed image – #885 - Fix
ImageProcessors.Resizeignoringupscalewhencropistrue, which enlarged the image beyond its native resolution – #886 - Fix the
fileanddataURL schemes being matched case-sensitively, soFILE:///…andData:…URLs skipped the local resource fast path and had their contents copied into the data cache – #887 - Fix a crash when resizing an image to a non-finite size.
CGContextcreation converted the dimensions withInt(_:), which traps on NaN and infinity – #888 - Fix
ImageDecoders/Defaultgenerating GIF previews even when the preview policy isImagePipeline/PreviewPolicy/disabled– #892 - Fix
ImageProcessors/GaussianBlurblurring the image with a radius of0or less instead of returning it unchanged. The radius was clamped to a minimum of2, so every radius in0...2also rendered identically under different cache keys – #893 - Fix
ImageProcessingOptions/Borderproducing the same identifier for every color with no sRGB representation, e.g. a pattern color. The components are now also clamped to0...1, which used to produce malformed hex for wide-gamut colors on iOS – #891 - Fix progressive previews being silently dropped for the rest of the task's lifetime when the cached data fails to decode and the task falls back to loading the image – #894
- Fix
DataLoader/delegatenever receivingurlSession(_:didCreateTask:)on macOS and Mac Catalyst, where the forwarding was compiled out – #895 - Fix
ImageProcessors/GaussianBlurtagging opaque images with an alpha channel, which madeImageEncoders/Defaultencode blurred photos as PNG instead of JPEG or HEIC – #896 - Fix an outstanding progressive decoding or processing operation not being cancelled when a task fails, leaving it to occupy a queue slot and hold on to the decoded preview until the queue drained past it – #898
- Fix a late image response reaching the memory cache after its task had already ended, letting a stale progressive scan overwrite the entry – #898
New Contributors
- @nathantannar4 made their first contribution in #884
Full Changelog: 13.1.0...13.2.0
Nuke 13.1.0
What's Changed
- Fix
GaussianBlurcrash on grayscale images by @Delarkz in #880 - Fix cache initialization for mergeable libraries by @ZHUOLIN0928 in #881
- Add asynchronous image decoding support by @thliu21 in #879
- Add
ImageDecoderRegistry.unregister(_:).register(_:)now returns a discardableRegistrationTokenfor removing an individual decoder instead of clearing the entire registry ImageDecoderRegistry.register(_:)now takes a@Sendableclosure. The registry is shared between the pipelines and the closures are called on the decoding threads- Fix a data race in
ImageDecoderRegistrywheredecoder(for:)iterated the registered decoders without holding the lock. The registry is now backed byMutexand isSendableinstead of@unchecked Sendable - Fix a leak where requests that finish synchronously (memory cache hits, disk cache hits for
data(for:), local resources,.returnCacheDataDontLoadmisses, and malformed URLs) were never removed from the pipeline, retaining their responses for its lifetime - Fix
ImageTask.Event.startedbeing delivered toImagePipeline.Delegateafter.finishedfor requests that finish synchronously - Fix
DataLoadercallingcompletiontwice when response validation fails, violating theDataLoadingcontract - Fix a request cancelled while
ImagePipeline.Delegate.willLoadData(for:urlRequest:pipeline:)was suspended still running the download to completion, discarding the result and occupying a slot indataLoadingQueue - Fix resumable data being lost when a request ended before the server responded. It's now put back if nothing new was downloaded
- Fix requests with
ImageRequest.Options.skipDataLoadingQueuenot cancelling the underlyingTask, leaving an asyncwillLoadDataor a customdatafetch closure running after cancellation - Fix progressive previews being permanently disabled when the first downloaded chunk isn't enough to tell that the image supports them, which is common for progressive JPEGs with large EXIF/ICC preambles. The policy is now re-evaluated as more data arrives, but only until it starts producing previews and with a hard cap on the number of attempts
- Fix
ImageDecoders.Emptynot marking the containers returned bydecodePartiallyDownloadedData(_:)as previews, so the truncated data was stored in the caches and delivered as a completed response on the subsequent cache hits - Fix
ImageProcessors.CoreImageFiltersharing a single mutableCIFilterbetween concurrently processed images.CIFilteris not thread-safe, so the processor could produce an image from another request's input and cache it under the wrong key. It now applies a copy of the filter - Fix the screen scale used by
ImageProcessingOptions.Unit.pointsbeing read once and cached for the lifetime of the process.UITraitCollection.currentreports a0display scale outside of the contexts managed by UIKit, so if the first points-basedImageRequestwas created on a background thread, every subsequent resize failed withprocessingFailedand the point-based border widths and corner radii became0 - Fix a crash on macOS when an
ImageProcessingOptions.Borderis created with anNSColoroutside an RGB colorspace, including.black,.white, and the catalog colors such as.labelColor. UnlikeUIColor,NSColorraises fromgetRed(_:green:blue:alpha:)instead of converting on the fly, so merely reading the processoridentifierterminated the process. The color is now converted to sRGB first - Fix
ImagePrefetcher.stopPrefetching(with:)andImagePrefetcher.stopPrefetching()doing nothing for the prefetches that were scheduled but haven't started yet. In the standard collection view pattern, the requests ran the entire download/decode/cache cycle anyway - Fix
ImagePrefetcher.prioritynot reaching the prefetches that were scheduled but haven't started yet. They kept the priority frozen in their original requests and eventually did the actual work at the old priority - Fix the async/await variant of
FetchImage.load(_:)delivering a result after the load was cancelled or superseded, overwriting the state of the newer load - Fix
LazyImagewith thelowerPrioritydisappear behavior permanently lowering the priority of its requests after the view goes off-screen. The override wasn't reset when the view reappeared - Fix
LazyImageViewstacking the views created bymakeImageView. A view was added for every displayed response without removing the previous one, so the orphaned views survived cell reuse, covered the newer content, and retained their images
New Contributors
- @Delarkz made their first contribution in #880
- @ZHUOLIN0928 made their first contribution in #881
- @thliu21 made their first contribution in #879
Full Changelog: 13.0.6...13.1.0
Nuke 13.0.6
- Fix crashes on the Swift 5 boundary caused by dynamic actor isolation when invoking
Optional.mapwith closures inferred as@MainActorinLazyImage,LazyImageView,FetchImage, andloadImage(with:into:)
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