-
Notifications
You must be signed in to change notification settings - Fork 3
Nuke
Package
SwiftBindings.Nuke· Upstream13.0.6Auto-published fromlibraries/Nuke/NUKE-GUIDE.md.
SwiftBindings.Nuke exposes Nuke — Kean's image loading and caching framework for Apple platforms — to C# through .NET 10's native Swift interop. These are direct Swift calls, not Objective-C proxy wrappers.
Nuke's core model translates almost one-for-one: you build an ImageRequest, hand it to an ImagePipeline, and get a UIImage (or NSImage on macOS) back. Swift's async/await surfaces as Task<T> with CancellationToken support, so the loading path feels idiomatic in C#. The corners that don't translate cleanly — nested-type renames, the built-in processors, OptionSet flags — are covered explicitly below.
This guide is written against the bindings generated for Nuke 13.0.6 with SwiftBindings.Sdk 0.19.0.
- Requirements & install
- Naming conventions
- Quick start: load an image
- The pipeline
- Requests and options
- Cache management
- Prefetching
- Tasks, progress, and cancellation
- Processors
- Errors
- Known limitations
- Memory & threading
- Reference links
- .NET 10.0+
-
Multi-TFM package —
net10.0-ios,net10.0-macos,net10.0-tvos - Deployment minimums: iOS 15.0, macOS 12.0, tvOS 15.0 (set your app's own floor with
<SupportedOSPlatformVersion>) - macOS host for development
dotnet add package SwiftBindings.Nuke
using Nuke;
using Swift; // runtime types (SwiftResult, CGSize, …)
using Swift.Runtime; // ISwiftObject, SwiftObjectHelper<T>Child namespaces aren't imported by
using Nuke;. Nuke's caseless-enum containers —ImageProcessors,ImageDecoders,ImageProcessingOptions— project as child namespaces ofNuke, not as static types.using Nuke;brings in the top-level types (ImagePipeline,ImageRequest,ImageCache, …) but not those. Reference them fully-qualified or alias them:using ImageProcessors = Nuke.ImageProcessors; using ImageDecoders = Nuke.ImageDecoders; using ImageProcessingOptions = Nuke.ImageProcessingOptions;
Platform image type. The binding is generated per-TFM against the platform's native image type, so the same call has a different return type depending on your target:
| TFM | Image type |
|---|---|
net10.0-ios, net10.0-tvos
|
UIKit.UIImage |
net10.0-macos |
AppKit.NSImage |
In multi-targeted code, guard with #if IOS || TVOS / #if MACOS, or type the result with var.
| Swift | C# | Rule |
|---|---|---|
ImagePipeline.Configuration (nested type) + pipeline.configuration (property) |
type → ImagePipeline.ConfigurationInfo; property stays Configuration
|
A nested type whose name collides with a member gets a disambiguating suffix. Non-enum types get Info; enums get Kind. The member keeps its natural name. |
ImagePipeline.Cache / pipeline.cache
|
ImagePipeline.CacheInfo / .Cache
|
same rule |
ImageRequest.Options / request.options
|
ImageRequest.OptionsInfo / .Options
|
same rule |
ImageRequest.Priority / request.priority
|
ImageRequest.PriorityKind / .Priority
|
enum → Kind suffix |
ImageTask.State / task.state
|
ImageTask.StateKind / .State
|
enum → Kind suffix |
ImageResponse.CacheType / response.cacheType
|
ImageResponse.CacheTypeKind / .CacheType
|
enum → Kind suffix |
func image(for:) async throws -> UIImage |
Task<UIImage> ImageAsync(…, CancellationToken) |
async methods get an Async suffix and an optional CancellationToken
|
func imageTask(with:) -> ImageTask |
ImageTask ImageTask(ImageRequest) |
method names are PascalCased; collisions with the return type are fine in C# |
func trim(toCost:) / func trim(toCount:)
|
TrimToCost(nint) / TrimToCount(nint)
|
Swift overloads that would collide in C# are named from their argument labels. ImageCache.Trim is the only place in this binding where that applies. |
let (data, response) = try await pipeline.data(for:) |
Task<(byte[], NSUrlResponse?)> DataAsync(…) |
Swift tuple returns become C# value tuples |
Data |
byte[] |
Foundation.Data parameters/returns project as byte[]
|
URL / URLRequest
|
Foundation.NSUrl / Foundation.NSUrlRequest
|
Foundation reference types bridge through ObjC |
TimeInterval? |
double? |
Swift optionals become C# nullables |
enum DataCachePolicy (no payload) |
enum DataCachePolicy : int |
payload-free Swift enums become plain C# enums |
struct Options: OptionSet |
class with RawValue + static members + new OptionsInfo(ushort) + | & ^ ~ Contains
|
option sets get their set operators — see below |
ImagePipeline.Error (enum with payloads) |
class with a CaseTag + static singletons + TryGet… accessors |
payload-carrying Swift enums project as a class |
Two structural notes that follow from the table:
Option sets combine with the usual operators. ImageRequest.OptionsInfo and ImagePipeline.CacheInfo.Caches are Swift OptionSets, and the generator emits |, &, ^, ~ and Contains alongside the named members and RawValue:
var opts = ImageRequest.OptionsInfo.DisableMemoryCacheReads
| ImageRequest.OptionsInfo.DisableDiskCacheWrites;
bool skipsMemoryReads = opts.Contains(ImageRequest.OptionsInfo.DisableMemoryCacheReads);ImagePipeline.CacheInfo.Caches works the same way but with nint RawValue and a new Caches(nint) constructor. It already ships Memory, Disk, and All, which covers most uses.
Each operator allocates. These option sets project as C# classes, so every
|/&/^/~builds a fresh native instance and the intermediates in a chain (a | b | c) are only reclaimed by the finalizer. That is fine for the once-per-request use above; don't build option sets in a hot loop without holding and disposing the result. The raw-value form (new ImageRequest.OptionsInfo((ushort)(a.RawValue | b.RawValue))) is still available and allocates once.
Payload enums are classes. ImagePipeline.Error has static singletons for payload-free cases (Error.Cancelled, Error.DataIsEmpty, Error.PipelineInvalidated, …), static factories for payload cases (Error.DataLoadingFailed(err)), a Tag property of type Error.CaseTag, and TryGet… accessors that unpack payloads.
using Nuke;
var pipeline = ImagePipeline.Shared;
using var request = new ImageRequest("https://example.com/photo.jpg");
UIKit.UIImage image = await pipeline.ImageAsync(request);
imageView.Image = image;ImageAsync takes an optional CancellationToken, and there's an NSUrl overload when you don't need a full request:
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
try
{
var image = await pipeline.ImageAsync(new Foundation.NSUrl(url), cts.Token);
}
catch (OperationCanceledException)
{
// cancelled; the underlying Swift task is cancelled too
}Concurrent loads are just Task.WhenAll — the pipeline coalesces duplicate requests internally:
var images = await Task.WhenAll(
urls.Select(u => pipeline.ImageAsync(new ImageRequest(u))));To fetch the raw bytes without decoding, use DataAsync, which returns a tuple:
var (data, urlResponse) = await pipeline.DataAsync(request);
Console.WriteLine($"{data.Length} bytes, {urlResponse?.Url}");ImagePipeline.Shared is the default instance and is settable — the usual way to install app-wide configuration:
var config = new ImagePipeline.ConfigurationInfo
{
DataCachePolicy = ImagePipeline.DataCachePolicy.StoreAll,
IsProgressiveDecodingEnabled = true,
IsTaskCoalescingEnabled = true,
IsRateLimiterEnabled = true,
};
config.DataCache = new DataCache("com.myapp.images");
ImagePipeline.Shared = new ImagePipeline(config);Nuke 13 removed the parameterless
ImagePipeline(). A configuration is always required:new ImagePipeline(new ImagePipeline.ConfigurationInfo())is the "defaults" form. The constructor also takes an optionalImagePipeline.IDelegate.
Two ready-made configurations are exposed as static properties, plus a factory that names the on-disk cache:
var httpCached = ImagePipeline.ConfigurationInfo.WithURLCache; // HTTP disk cache
var aggressive = ImagePipeline.ConfigurationInfo.WithDataCache; // Nuke DataCache, 150 MB
var named = ImagePipeline.ConfigurationInfo.WithDataCacheMethod("com.myapp.images", sizeLimit: 200_000_000);Notable ConfigurationInfo members (all get/set unless noted):
| Member | Type | |
|---|---|---|
DataLoader |
IDataLoading |
defaults to Nuke's DataLoader; you can read it but not build a replacement — see Known limitations
|
DataCache |
IDataCaching? |
disk cache; null disables it |
ImageCache |
IImageCaching? |
memory cache |
DataCachePolicy |
ImagePipeline.DataCachePolicy |
Automatic / StoreOriginalData / StoreEncodedImages / StoreAll
|
IsTaskCoalescingEnabled |
bool |
dedupe identical in-flight requests |
IsDecompressionEnabled, IsRateLimiterEnabled, IsProgressiveDecodingEnabled, IsStoringPreviewsInMemoryCache, IsResumableDataEnabled, IsLocalResourcesSupportEnabled
|
bool |
behaviour toggles (the last covers file:// / data:// URLs) |
MaximumResponseDataSize |
int? |
|
DataLoadingQueue, ImageDecodingQueue, ImageProcessingQueue, ImageEncodingQueue, ImageDecompressingQueue
|
TaskQueue |
concurrency knobs |
IsSignpostLoggingEnabled |
static bool |
os_signpost instrumentation |
pipeline.Configuration and pipeline.Cache are read-only. pipeline.Invalidate() cancels outstanding work and rejects new requests.
DataLoader also exposes the underlying URLSession bits:
Foundation.NSUrlSessionConfiguration cfg = DataLoader.DefaultConfiguration;
Foundation.NSUrlCache urlCache = DataLoader.SharedUrlCache;The simplest constructor takes a URL string:
using var request = new ImageRequest("https://example.com/photo.jpg");Everything else is mutable after construction, which is usually easier than the wide constructors:
using var request = new ImageRequest(url)
{
Priority = ImageRequest.PriorityKind.High,
Scale = 2.0f,
ImageID = "avatar-42", // custom cache key
Thumbnail = new ImageRequest.ThumbnailOptions(maxPixelSize: 400f),
};PriorityKind is VeryLow, Low, Normal, High, VeryHigh.
Read-only properties worth knowing: Url (NSUrl?), UrlRequest (NSUrlRequest?), Description, Processors (IReadOnlyList<IImageProcessing>), UserInfo.
The wide constructors exist if you prefer them — note that processors is not optional in those overloads, so pass an empty array when you don't need any. There is a matching set taking an NSUrlRequest, which is how you attach custom headers:
using var request = new ImageRequest(
new Foundation.NSUrl(url),
Array.Empty<IImageProcessing>(),
ImageRequest.PriorityKind.High,
ImageRequest.OptionsInfo.DisableDiskCacheWrites);
var urlRequest = new Foundation.NSMutableUrlRequest(new Foundation.NSUrl(url));
urlRequest.Headers = Foundation.NSDictionary.FromObjectAndKey(
new Foundation.NSString("Bearer …"), new Foundation.NSString("Authorization"));
using var authed = new ImageRequest(urlRequest, Array.Empty<IImageProcessing>());OptionsInfo flags: DisableMemoryCacheReads, DisableMemoryCacheWrites, DisableMemoryCache, DisableDiskCacheReads, DisableDiskCacheWrites, DisableDiskCache, ReloadIgnoringCachedData, ReturnCacheDataDontLoad, SkipDecompression, SkipDataLoadingQueue. Combine them with | as shown in Naming conventions.
Thumbnails decode at a reduced size and are much cheaper than loading full-resolution images you're going to shrink anyway:
using var thumb = new ImageRequest.ThumbnailOptions(maxPixelSize: 200f);
// or size-based:
using var thumb2 = new ImageRequest.ThumbnailOptions(
new Swift.CGSize(200, 200),
ImageProcessingOptions.Unit.Points,
ImageProcessingOptions.ContentMode.AspectFill);
request.Thumbnail = thumb;Nuke has three cache layers, reachable three different ways.
1. The unified façade — pipeline.Cache (ImagePipeline.CacheInfo). This is the one you want most of the time; it queries memory and disk together and speaks ImageRequest:
var cache = ImagePipeline.Shared.Cache;
bool cached = cache.ContainsCachedImage(request);
ImageContainer? container = cache.CachedImage(request);
if (container is not null)
imageView.Image = container.Image;
cache.StoreCachedImage(container!, otherRequest);
cache.RemoveCachedImage(request);
cache.RemoveAll();Every method has an overload taking ImagePipeline.CacheInfo.Caches (Memory, Disk, All) to scope the operation:
cache.RemoveAll(ImagePipeline.CacheInfo.Caches.Memory);
bool onDisk = cache.ContainsCachedImage(request, ImagePipeline.CacheInfo.Caches.Disk);Raw encoded data lives behind CachedData / StoreCachedData / ContainsData / RemoveCachedData; key derivation is exposed via MakeImageCacheKey(request) and MakeDataCacheKey(request); and cache[request] / cache[nsUrl] indexers read and evict (cache[request] = null;).
2. Memory cache — ImageCache.
var mem = ImageCache.Shared; // read-only static; construct your own to replace it
mem.CostLimit = 50_000_000; // bytes
mem.CountLimit = 200;
mem.Ttl = 300.0; // double?, seconds
mem.EntryCostLimit = 0.1;
int count = mem.TotalCount;
int cost = mem.TotalCost;
mem.TrimToCost(10_000_000); // Swift's trim(toCost:)
mem.TrimToCount(100); // Swift's trim(toCount:)
mem.RemoveAll();Constructors: new ImageCache(), new ImageCache(nint costLimit), new ImageCache(nint costLimit, nint countLimit). Entries are addressable by key — using var key = new ImageCacheKey(request); var hit = mem[key];.
3. Disk cache — DataCache. A standalone key/value store on disk, keyed by string:
var disk = new DataCache("com.myapp.images");
disk.SizeLimit = 100_000_000;
disk.SweepInterval = 60.0;
disk.IsSweepEnabled = true;
disk.StoreData(bytes, "my-key");
bool has = disk.ContainsData("my-key");
byte[]? back = disk.CachedData("my-key");
disk.RemoveData("my-key");
disk.Flush(); // force pending writes
disk.Sweep(); // enforce the size limit now
disk.RemoveAll();
Foundation.NSUrl path = disk.Path;
int bytesUsed = disk.TotalSize;Also available: new DataCache(NSUrl path), the filenameGenerator overloads, DataCache.Filename(key), disk.Url(key), and a this[string] indexer.
DataCache.IsCompressionEnabledwas removed in Nuke 13.0 — compression is now a separate concern. Any migration guide or blog post showing it predates 13.0.
var prefetcher = new ImagePrefetcher(
ImagePipeline.Shared,
ImagePrefetcher.Destination.MemoryCache, // or .DiskCache — data only, no decode
maxConcurrentRequestCount: 2);
prefetcher.Priority = ImageRequest.PriorityKind.Low;
prefetcher.StartPrefetching(urls.Select(u => new Foundation.NSUrl(u)).ToArray());
// or with full requests:
prefetcher.StartPrefetching(requests);
prefetcher.IsPaused = true; // e.g. while the user is scrolling fast
prefetcher.StopPrefetching(requests); // scope to specific items
prefetcher.StopPrefetching(); // cancel everythingprefetcher.DidComplete is an Action? callback fired when the queue drains.
Destination.DiskCache skips decoding entirely (cheaper CPU) but is incompatible with DataCachePolicy.Automatic for requests with processors, and with StoreEncodedImages.
pipeline.ImageTask(request) starts a load and hands back an ImageTask you can steer:
var task = ImagePipeline.Shared.ImageTask(request);
task.Priority = ImageRequest.PriorityKind.VeryHigh; // re-prioritise mid-flight
ImageTask.Progress p = task.CurrentProgress;
Console.WriteLine($"{p.Completed}/{p.Total} ({p.Fraction:P0})");
var image = await task.GetImageAsync(); // or GetResponseAsync()
task.Cancel();task.State is ImageTask.StateKind (Running, Cancelled, Completed). task.Request returns the originating ImageRequest; task.TaskId is a stable ulong.
For streaming progress and progressive previews, task.Events is an IAsyncEnumerable<ImageTask.Event>:
await foreach (var evt in task.Events)
{
switch (evt.Tag)
{
case ImageTask.Event.CaseTag.Progress
when evt.TryGetProgress(out var progress):
UpdateProgressBar(progress.Fraction);
break;
case ImageTask.Event.CaseTag.Preview
when evt.TryGetPreview(out var preview):
imageView.Image = preview.Image; // progressive JPEG scan
break;
case ImageTask.Event.CaseTag.Finished
when evt.TryGetFinished(out var result):
// result is SwiftResult<ImageResponse, ImagePipeline.Error>
break;
}
}Callback-style loading is also bound, returning a SwiftResult:
var task = ImagePipeline.Shared.LoadImage(request, result =>
{
// result: SwiftResult<ImageResponse, ImagePipeline.Error>
});An ImageResponse carries Image, Container, Request, UrlResponse, IsPreview, and CacheType (ImageResponse.CacheTypeKind? — Memory, Disk, or null for a network load). ImageContainer adds Data, Type (AssetType? — Png, Jpeg, Gif, Heic, Webp, Mp4, M4v, Mov, Ico), and UserInfo.
The built-in processors under the ImageProcessors namespace construct and run:
using ImageProcessors = Nuke.ImageProcessors;
using var resize = new ImageProcessors.Resize(width: 200.0);
using var resize2 = new ImageProcessors.Resize(
new Swift.CGSize(200, 200),
ImageProcessingOptions.Unit.Points,
ImageProcessingOptions.ContentMode.AspectFill,
crop: true);
using var circle = new ImageProcessors.Circle();
using var rounded = new ImageProcessors.RoundedCorners(radius: 10.0);
using var blur = new ImageProcessors.GaussianBlur(radius: 8);
UIKit.UIImage? processed = resize.Process(sourceImage);Each exposes Identifier, Description, and Process(image). RoundedCorners and Circle take an optional ImageProcessingOptions.Border(UIColor color, double width = 1, Unit unit = Points). ImageProcessingOptions.Unit is Points / Pixels; ContentMode is AspectFill / AspectFit; both have a GetDescription() extension.
Every built-in processor implements IImageProcessing, so they can also be handed to the pipeline rather than applied by hand:
using var request = new ImageRequest(
new Foundation.NSUrl(url),
new IImageProcessing[] { new ImageProcessors.Resize(width: 200.0),
new ImageProcessors.RoundedCorners(radius: 10.0) });
using var chained = new ImageProcessors.Composition(
new IImageProcessing[] { resize, circle });Two
IImageProcessingmembers come from a Swift protocol extension and only work on the concrete type.HashableIdentifierand theProcess(ImageContainer, ImageProcessingContext)overload are declared on the interface as default implementations that throwNotSupportedException— Swift supplies them from an extension, which has no witness to dispatch through. Call them onResize/Circle/ … directly.IdentifierandProcess(UIImage)are real requirements and dispatch normally.
CoreImageFilter bridges arbitrary Core Image filters:
using var sepia = new ImageProcessors.CoreImageFilter("CISepiaTone");
var output = sepia.Process(sourceImage);
// or apply a configured CIFilter directly:
var applied = ImageProcessors.CoreImageFilter.Apply(myCIFilter, sourceImage);Decoders are similar — new ImageDecoders.Default() and new ImageDecoders.Empty() both construct and expose Decode(byte[]) / DecodePartiallyDownloadedData(byte[]). Note that registering one is not reachable from C#: ImageDecoderRegistry.Register only exists in its closure-matching form, which is an [Obsolete("SB0005")] stub (see Known limitations). ImageDecoderRegistry.Shared.Clear() works.
Pipeline failures surface as ImagePipeline.Error, a class projecting Nuke's payload-carrying Swift enum.
Payload-free cases are static singletons — Error.Cancelled, Error.DataIsEmpty, Error.DataMissingInCache, Error.ImageRequestMissing, Error.PipelineInvalidated, Error.DataDownloadExceededMaximumSize. Payload cases are unpacked with TryGet…:
if (error.Tag == ImagePipeline.Error.CaseTag.DataLoadingFailed
&& error.TryGetDataLoadingFailed(out var underlying))
{
Console.WriteLine(underlying); // Swift.Foundation.AnyError
}
// shortcut for the common case:
Swift.Foundation.AnyError? cause = error.DataLoadingError;
Console.WriteLine(error.Description);The other cases are DecoderNotRegistered(context), DecodingFailed(decoder, context, error), and ProcessingFailed(processor, context, error).
On the async path (ImageAsync / DataAsync) a failure throws; OperationCanceledException is what you get from a cancelled CancellationToken. DataLoader.Error.StatusCodeUnacceptable(code) covers HTTP status rejections, and DataLoader.Validate(response) is exposed as a static if you want to run the default validation yourself.
The binding surface is close to complete. What isn't reachable:
-
IImageProcessing.HashableIdentifierandProcess(container, context)throw through the interface — they are Swift protocol-extension defaults with no witness to dispatch through (see Processors). Use the concrete type. -
ImagePipeline(delegate:configure:)is emitted as an[Obsolete("SB0009")]stub (an optional wider than one machine word with no wrapper) and throwsNotSupportedException. Build aConfigurationInfoand usenew ImagePipeline(config, delegate)instead. -
A C#-authored
ImagePipeline.IDelegateis never called back.new ImagePipeline(config, delegate)itself works, but the parameter carries an[Obsolete("SB0008")]warning: the existential's reverse-dispatch proxy could not be generated, so only a delegate value that originated in Swift round-trips. Configure behaviour throughConfigurationInfoinstead. -
A C#-authored
IDataLoadingis never called back either. The whole interface carries[Obsolete("SB0010")]— no requirement of Swift'sDataLoadingis reverse-dispatchable, so a custom loader written in C# compiles, installs, and never fires. Consuming Nuke's ownDataLoaderthrough the interface is unaffected. UseDataLoaderand itsURLSessionConfigurationknobs. -
DataLoadercannot be constructed from C#, and decoders cannot be registered. Both types expose exactly one initializer/registration entry point and both take a closure the bridge can't marshal, so both are[Obsolete("SB0005")]stubs that throwNotSupportedException:new DataLoader(configuration, validate)(thevalidateclosure returns anError?) andImageDecoderRegistry.Register(match). Use theDataLoaderthe pipeline already installs — read it fromConfigurationInfo.DataLoader, and reach the session knobs through theDataLoader.DefaultConfiguration/DataLoader.SharedUrlCachestatics.ImageDecoderRegistry.Clear()works;Decoder(context)is[Obsolete("SB0001")]. -
ImagePipeline.LoadImage(request, progress, completion)— the 3-argument progress-reporting overload — is[Obsolete("SB0001")](no@_cdeclwrapper). It is soft-deprecated upstream in Nuke 12.9 anyway; usetask.Eventsfor progress. -
Protocol-typed values are restricted. When you hold a
IDataCaching/IImageCaching/IImageDecoding/IImageEncoding/IImageProcessing/IDataLoadinginterface reference rather than a concrete type, members whose signature carriesFoundation.Data, an optional return, a subscript, or a closure are[Obsolete("SB0003")]and throw. Concrete types (DataCache,ImageCache,ImageDecoders.Default) have no such restriction — call those directly. This mainly bites if you assign your own cache toconfig.DataCacheand then try to read it back through the interface. -
Deprecated-upstream members are carried through as
[Obsolete]:ConfigurationInfo.MaximumDecodedImageSize(useImageRequest.ThumbnailOptions),ImageRequest.ImageId(useImageID), and theUserInfoKey.ImageIdKey/ScaleKey/ThumbnailKeyconstants. -
ImageCache.Sharedis read-only, unlikeImagePipeline.Shared. To install a custom memory cache, setConfigurationInfo.ImageCacheand build a pipeline. -
macOS is metadata-verified, not exercised. The macOS test app validates that the
macos-arm64_x86_64slice loads and Swift metadata resolves for the core types; the full loading workflows are exercised on iOS Simulator (Mono JIT) and physical device (NativeAOT). tvOS is build-only.
Generated Nuke types implement ISwiftObject / IDisposable. using var is the recommended pattern for deterministic cleanup — Dispose is safe on every generated type and double-Dispose is a no-op.
-
Struct-backed types especially.
ImageRequest,ImageResponse,ImageContainer,OptionsInfo,ThumbnailOptions,ConfigurationInfo,CacheInfo, and the processor types wrap Swift structs and hold a native buffer. Creating them in a hot loop without disposing leaks until the finalizer runs. -
Writes through a struct-valued property hit a copy. Because those structs project as classes,
pipeline.Configuration.IsRateLimiterEnabled = falsecompiles, runs, and changes nothing — the getter handed you a temporary. Build the value, mutate it, then assign the whole thing (ImagePipeline.Shared = new ImagePipeline(config)). The SDK'sSB1003analyzer warning flags this shape at compile time. -
Long-lived objects.
ImagePipeline,ImageCache,DataCache, andImagePrefetcherare reference types you normally keep for the app's lifetime — don't wrap those inusing. -
Don't dispose static singletons.
ImagePipeline.Shared,ImageCache.Shared,ImageDecoderRegistry.Shared,ImagePipeline.Error.Cancelled, and theOptionsInfo/AssetTypestatic members return cached instances. -
Sendable. Most Nuke value types are marked[SwiftSendable]and may be shared across .NET threads. The pipeline itself is thread-safe by design —ImageAsyncfrom multiple threads concurrently is the intended usage and is validated in the test app. -
Callbacks run off the main thread.
LoadImagecompletions,task.Events, andprefetcher.DidCompletefire on Nuke's internal queues. Marshal to the main thread before touching UIKit/AppKit.
For full API semantics, behaviour of the caching layers, and performance guidance, consult the upstream documentation — this guide only covers the C# translation.
- Nuke — official documentation — DocC reference for the whole framework
- Nuke — Getting Started
- Nuke — Performance Guide
- kean/Nuke on GitHub — source, releases, migration guides
- Nuke migration guides — including the 12 → 13 changes reflected in this binding
-
ActivityKit —
SwiftBindings.Apple.ActivityKitv26.2.9 -
CryptoKit —
SwiftBindings.Apple.CryptoKitv26.2.9 -
FamilyControls —
SwiftBindings.Apple.FamilyControlsv26.2.9 -
LiveCommunicationKit —
SwiftBindings.Apple.LiveCommunicationKitv26.2.9 -
Matter —
SwiftBindings.Apple.Matterv26.2.9 -
MatterSupport —
SwiftBindings.Apple.MatterSupportv26.2.9 -
MusicKit —
SwiftBindings.Apple.MusicKitv26.2.9 -
ProximityReader —
SwiftBindings.Apple.ProximityReaderv26.2.9 -
RealityFoundation —
SwiftBindings.Apple.RealityFoundationv26.2.9 -
RealityKit —
SwiftBindings.Apple.RealityKitv26.2.9 -
RoomPlan —
SwiftBindings.Apple.RoomPlanv26.2.9 -
StoreKit2 —
SwiftBindings.Apple.StoreKit2v26.2.9 -
TipKit —
SwiftBindings.Apple.TipKitv26.2.9 -
Translation —
SwiftBindings.Apple.Translationv26.2.9 -
WeatherKit —
SwiftBindings.Apple.WeatherKitv26.2.9 -
WorkoutKit —
SwiftBindings.Apple.WorkoutKitv26.2.9
-
BlinkID —
SwiftBindings.BlinkIDupstream 7.8.0 -
BlinkIDUX —
SwiftBindings.BlinkIDUXupstream 7.8.0 -
Facebook —
SwiftBindings.Facebook.*upstream 18.1.0 -
Kingfisher —
SwiftBindings.Kingfisherupstream 8.11.0 -
Lottie —
SwiftBindings.Lottieupstream 4.6.1 -
MapLibre —
SwiftBindings.MapLibreupstream 6.28.0 -
Mappedin —
SwiftBindings.Mappedinupstream 6.7.0 -
Nuke —
SwiftBindings.Nukeupstream 13.0.6 -
Stripe —
SwiftBindings.Stripe.*upstream 26.4.1