Skip to content

Add asynchronous image decoding support - #879

Merged
kean merged 2 commits into
kean:mainfrom
thliu21:codex/860-async-image-decoding
Aug 9, 2026
Merged

Add asynchronous image decoding support#879
kean merged 2 commits into
kean:mainfrom
thliu21:codex/860-async-image-decoding

Conversation

@thliu21

@thliu21 thliu21 commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add AsyncImageDecoding for decoders that need to call asynchronous APIs
  • await async decoders on the existing image decoding queue while preserving error wrapping, cancellation, priority, and signpost behavior
  • keep existing synchronous ImageDecoding implementations source-compatible
  • document async decoder registration and the synchronous cache API limitation
  • add coverage for successful async decoding and error propagation

Motivation

ImageDecoding currently exposes only a synchronous entry point, so implementations backed by async APIs have to block a thread with a semaphore. This adds a dedicated async refinement that the pipeline can detect and await without changing existing decoders.

Closes #860.

Testing

  • swift build (all package targets)
  • ImagePipelineDecodingTests: 5 passed
  • ImagePipelineTests and ImagePipelineProgressiveDecodingTests: 35 passed
  • DocC build

The macOS test runs used Thread Sanitizer disabled because the local Xcode 26.3 test runner could not load its TSan runtime; the shared scheme remains unchanged.

@thliu21
thliu21 marked this pull request as ready for review July 10, 2026 17:54
@kean

kean commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Hey, thanks for the PR! This is a problem worth solving, and I've been thinking about it on and off.

A few thoughts in no particular order:

  • Introducing a new AsyncImageDecoding protocol is a good option: it's a relatively niche use case, and this approach keeps the change non-breaking.
  • There are scenarios it doesn't cover, such as ImagePipeline.Cache.cachedImage(for:caches:), which relies on decoding being synchronous.
  • As a workaround, you can currently use semaphores. It's not great, but not terrible.

I'm also considering a more comprehensive change: making the base ImageDecoding protocol async, along with ImagePipeline.Cache.cachedImage(for:caches:) and other APIs like ImageProcessing, DataLoading, and more. That's what you'd expect from a modern framework. One thing I'm still on the fence about is whether to isolate these APIs to ImagePipelineActor to avoid paying for the thread hops. With Nuke 13 released almost half a year ago, now is probably a good time to start working on a new major version that includes this breaking change.

Update: thread hops in the case of ImageDecoding is less of a concern because the internal TaskQueue already supports async and uses it for all work.

@thliu21

thliu21 commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

Thanks — this matches the trade-off I was aiming for. cachedImage(for:caches:) is the known gap: because it is synchronous, an async-only decoder can’t participate in disk-cache decoding there without blocking. The PR calls this out in the decoding docs, while regular pipeline loads from the disk cache do await AsyncImageDecoding.

Your TaskQueue update also aligns with the implementation here: async decoding is enqueued directly on imageDecodingQueue, without introducing a separate background hop.

Given the broader next-major direction, would you prefer this PR as an interim non-breaking API—public or behind @_spi—or should we hold it and fold the work into making the base protocols async? I’m happy to adjust either way.

@kean

kean commented Aug 2, 2026

Copy link
Copy Markdown
Owner

@_spi

This is probably ideal. I keep forgetting about. I'm open to merging and shipping it with @_spi. I'd love to provide an escape hatch, but without "officially" supporting it just yet.

@kean
kean merged commit 062f1b4 into kean:main Aug 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Asynchronous image decoding

2 participants