Skip to content

Add a custom loading placeholder to CachedAsyncImage - #26

Open
Jeehut wants to merge 2 commits into
mainfrom
wip/cached-async-image-placeholder
Open

Jeehut wants to merge 2 commits into
mainfrom
wip/cached-async-image-placeholder

Conversation

@Jeehut

@Jeehut Jeehut commented Oct 27, 2026 •

Copy link
Copy Markdown
Member

Summary

CachedAsyncImage now accepts a custom view while an image loads. init(url:) keeps the existing ProgressView placeholder.

CachedAsyncImage(url: user.avatarURL) {
   Image(systemName: "person.crop.circle")
      .foregroundStyle(.secondary)
}
-public struct CachedAsyncImage: View
+public struct CachedAsyncImage<Placeholder: View>: View
+   init(url:placeholder:)
+extension CachedAsyncImage where Placeholder == ProgressView<EmptyView, EmptyView>
+   init(url:)

This is not a cache bug fix. The existing cache already shared one NSCache; its singleton and storage behavior remain unchanged. The non-generic cache stays outside the view so different placeholder types share it. Swift 6.4.2 rejects a static stored singleton in a type nested under a generic view. This does not prevent nesting the loader. The loader remains nested in the view; its phase stays nested in the loader. The platform image alias belongs to CachedAsyncImageCache.Image.

Keeping one cache avoids storing the same downloaded image separately for each placeholder type.

Evidence

  • Build succeeded. The full suite passed: 40 tests in 13 suites, explicitly without parallel testing.
  • All 6 CachedAsyncImageTests passed, including both placeholder initializers and cache reuse across different placeholder types.
  • The archived cache-regression run recorded 2 failed assertions: download count and image identity. The final run passed the same test.
  • Swift 6.4.2 AppKit and UIKit compiler probes accepted the nested types. A generic stored singleton failed with static stored properties not supported in generic types.
  • Evidence does not cover mounted UI or simulator runtime. UIKit evidence is compile-only.

Merge Danger

Door: one-way – public generic type change.

Blast Radius: consumers

CachedAsyncImage(url:) remains available. Explicit annotations such as let image: CachedAsyncImage need a placeholder type argument.

The internal nesting correction changes no public signature. Its code review covers only that correction, not approval of the whole PR.

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.

1 participant