Nuke Image Loading System
repository·main·Indexed 27 days ago
https://github.com/kean/nukeA high-performance, modular image loading system for Apple platforms (iOS, macOS, watchOS, tvOS, visionOS). Nuke provides a robust caching system, advanced image processing, and support for formats like HEIF, WebP, and GIF. It features a plugin-based architecture with modular components including NukeUI for SwiftUI and UIKit/AppKit, NukeExtensions, and NukeVideo for short video decoding and playback.
What's inside Nuke
- Nuke is a high-performance image loading system for Apple platforms (iOS, macOS, watchOS, tvOS, visionOS). It provides a robust caching system (memory and disk), advanced image processing, decompression, request coalescing, prefetching, and support for various formats including HEIF, WebP, and GIF. It is designed to be lean, fast to compile, and highly customizable via an extensible architecture.
Overview of NukeExtensions
mainNukeExtensions provides convenience extensions for image views, offering multiple display options to simplify image loading and presentation within your application.Overview of Nuke Image Format Support
mainNuke provides built-in support for standard image formats such as
jpeg,png, andheif. Beyond basic formats, Nuke's architecture allows for extending support to custom image formats and provides capabilities for:- Progressive decoding
- Animated image rendering
- Progressive animated image rendering
- Drawing vector images (directly or via bitmap conversion)
- Parsing thumbnails from image containers
Monitor performance with Pulse
mainTo monitor image loading performance, you can integrate the Pulse network logging framework by assigning a delegate to the
DataLoaderwithin theImagePipelineconfiguration.(ImagePipeline.shared.configuration.dataLoader as? DataLoader)?.delegate = URLSessionProxyDelegate()Extend Nuke with custom image decoding
mainTo support custom image formats for decoding, you can utilize Nuke's decoding infrastructure. This involves working with theImageDecodingprotocol and registering your custom decoders via theImageDecoderRegistry. The system usesImageDecodingContextto manage the decoding process and provides specific error types throughImageDecodingError.Install NukeUI for SwiftUI
mainTo display images in SwiftUI using Nuke, add theNukeUImodule to your project via Swift Package Manager alongside the coreNukelibrary.Implement image prefetching in UICollectionView
mainTo improve user experience by loading images ahead of time, use
UICollectionViewDataSourcePrefetchingin conjunction withImagePrefetcher.- Set
isPrefetchingEnabledtotrueon yourUICollectionView. - Set the
prefetchDataSourceto your view controller. - In
collectionView(_:prefetchItemsAt:), callprefetcher.startPrefetching(with:)using the URLs for the provided index paths. - In
collectionView(_:cancelPrefetchingForItemsAt:), callprefetcher.stopPrefetching(with:)to cancel tasks for items no longer in the prefetch window.
Warning: If you use
ImageProcessors(e.g.,ImageProcessors.Resize) when displaying images, you must use the exact same processors during prefetching. Otherwise, the prefetcher will cache the original image instead of the processed version, defeating the purpose of prefetching.final class PrefetchingDemoViewController: UICollectionViewController { private let prefetcher = ImagePrefetcher() private var photos: [URL] = [] override func viewDidLoad() { super.viewDidLoad() collectionView?.isPrefetchingEnabled = true collectionView?.prefetchDataSource = self } } extension PrefetchingDemoViewController: UICollectionViewDataSourcePrefetching { func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) { let urls = indexPaths.map { photos[$0.row] } prefetcher.startPrefetching(with: urls) } func collectionView(_ collectionView: UICollectionView, cancelPrefetchingForItemsAt indexPaths: [IndexPath]) { let urls = indexPaths.map { photos[$0.row] } prefetcher.stopPrefetching(with: urls) } }- Set
Share a DataCache between an app and an extension
mainTo share a disk cache between your main app and an extension (like a Notification Service Extension), point both to a directory within a shared App Group container. In the extension, set
isSweepEnabled = falseso that the extension does not attempt to enforce size limits or perform LRU cleanup; let the main app handle that.let sharedCacheURL = FileManager.default .containerURL(forSecurityApplicationGroupIdentifier: "group.com.myapp") // Main app ImagePipeline.shared = ImagePipeline { $0.dataCache = try? DataCache(path: sharedCacheURL) } // Extension — reads/writes the same cache but skips LRU sweeps ImagePipeline.shared = ImagePipeline { $0.dataCache = { let cache = try? DataCache(path: sharedCacheURL) cache?.isSweepEnabled = false return cache }() }Load images using ImagePipeline
mainThe
ImagePipelinehandles downloading, caching, and preparing images. You can load an image directly using the asyncimage(for:)method on thesharedpipeline.For more granular control, such as monitoring download progress, use
imageTask(with:)to create anImageTask. You can iterate over theprogressstream and then await theimageorresponseproperty.// Simple async load let image = try await ImagePipeline.shared.image(for: url) // Load with progress monitoring func loadImage() async throws { let imageTask = ImagePipeline.shared.imageTask(with: url) for await progress in imageTask.progress { // Update progress } imageView.image = try await imageTask.image }Monitor or modify DataLoader behavior
mainYou can access the
DataLoaderfrom theImagePipelineconfiguration to set adelegate. This is useful for monitoring network traffic (e.g., with Pulse) or handling authentication challenges viaURLSessionTaskDelegatemethods.Note that the
DataLoaderretains the delegate.// To monitor with Pulse: (ImagePipeline.shared.configuration.dataLoader as? DataLoader)?.delegate = URLSessionProxyDelegate() // To handle authentication challenges: (ImagePipeline.shared.configuration.dataLoader as? DataLoader)?.delegate = YourDelegate() final class YourDelegate: URLSessionTaskDelegate { func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { // Handle authentication challenge... } }Use LazyImage in SwiftUI with Nuke 12
mainIn Nuke 12,
LazyImageusesSwiftUI.Imageand requires acontentclosure to manage sizing and layout (similar toAsyncImage). Transition animations are disabled by default and must be provided via atransaction. Progress updates no longer triggercontentreloads; use anObservedObjectto handle progress independently.// Basic usage with custom sizing LazyImage(url: URL(string: "https://example.com/image.jpeg")) { state in if let image = state.image { image .resizable() .aspectRatio(contentMode: .fill) } } // Using animations LazyImage(url: URL(string: "https://example.com/image.jpeg"), transaction: .init(animation: .default)) { $0.image } // Handling progress without reloading content LazyImage(url: URL(string: "https://example.com/image.jpeg")) { state in if state.isLoading { ProgressView(state.progress) } } struct ProgressView: View { @ObservedObject var progress: FetchImage.Progress var body: some View { Text("progress.fraction * 100 %") } }Migrate custom ImageProcessing implementations
mainThe
ImageProcessingprotocol was updated to support caching processed images. Custom processors no longer conform toEquatabledirectly; instead, they must provide aStringidentifier(for cache keys) and ahashableIdentifier(AnyHashable) for efficient memory cache lookups. Additionally, theprocess(image:context:)method now accepts an optionalImageProcessingContext?.struct GaussianBlur: ImageProcessing, Hashable { let radius: Int func process(image: Image, context: ImageProcessingContext?) -> Image? { return /* create blurred image */ } // Prefer to use reverse DNS notation. var identifier: String { return "com.youdomain.processor.gaussianblur-\(radius)" } var hashableIdentifier: AnyHashable { return self } }