Accept asset catalog entries in localResources#465
Merged
Conversation
Generalises SuperwallOptions.localResources from `[String: URL]` to `[String: AssetResource]`. URL conforms to AssetResource so existing call sites are unaffected. New `CatalogAsset(name:bundle:)` registers a Data Set entry from an .xcassets, resolved at load time via NSDataAsset — the iOS equivalent of Android's R.raw.* resource IDs. ObjC keeps a URL-only shim under the same `localResources` name. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`UTType.preferredMIMEType` is iOS 14+. On iOS 13, fall back to `UTTypeCopyPreferredTagWithClass` from MobileCoreServices so catalog assets are served with a real MIME type (an `<img>` or `<video>` is refused by WKWebView when the MIME is `application/octet-stream`). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- SWLocalResourcesViewController: when a catalog asset exists but isn't image-decodable, show its UTI and byte count instead of a blank cell. - AssetResource: remove the unused UIKit import. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Satisfies the `trailing_closure` SwiftLint rule — the only remaining project-level lint violation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collaborator
Author
- LocalFileSchemeHandler: remove unreferenced UIKit import. - SuperwallOptions: ObjC setter now replaces only the URL subset of localResources, so CatalogAsset entries registered from Swift survive a subsequent ObjC assignment in mixed Swift/ObjC codebases. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
localResources is documented as set once before configure(), so the "mixed Swift/ObjC post-configure mutation" scenario the merge was guarding against isn't part of the intended usage pattern. The setter goes back to replacing the full dict. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
NSDataAsset only resolves Data Sets, so a typical Image Set logo would fail with "File not found". Try NSDataAsset first (lossless, any file type), then fall back to UIImage(named:in:compatibleWith:)?.pngData() so existing Image Sets work without restructuring the asset catalog. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Calling NSDataAsset on an Image Set triggers a CoreUI log about a wrong-typed lookup. Flip the order so UIImage(named:) runs first — Image Sets resolve cleanly, and NSDataAsset is only reached when there's no Image Set to mistype. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lets customers register an in-memory image directly: "logo": UIImage(named: "Logo")! Served to the webview as image/png via pngData(). Complements CatalogAsset for cases where eager decoding is acceptable. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collaborator
Author
Debug view was loading catalog assets via NSDataAsset only, so an Image Set CatalogAsset previewed as "Asset not found" even though the scheme handler resolved it successfully. Try UIImage(named:) first, fall through to NSDataAsset for Data Sets. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collaborator
Author
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Avoids re-encoding on every swlocal:// request, which runs on the main thread. Also updates the ObjC bridge doc to reflect that UIImage is accepted there too. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Changes in this pull request
Generalises
SuperwallOptions.localResourcesfrom[String: URL]to[String: AssetResource]so customers can register paywall assets that live inside an asset catalog (.xcassets), achieving parity with the Android SDK'sPaywallResource.FromResources(R.drawable.*).Customer ask (Pylon): customers whose assets live in
.xcassetshad no clean way to register them because asset catalog entries don't expose a file URL.API
New
AssetResourceprotocol is the dictionary's value type. Two conforming types:URL— existing behavior, a file on disk. Conforms out of the box, so existing call sites that assign URL dictionaries compile and behave unchanged.UIImage— register an in-memory image directly; served to the webview asimage/pngviapngData(). This covers the asset catalog case viaUIImage(named: "Logo").Implementation notes
LocalFileSchemeHandlerswitches on the resource type.URLis read withData(contentsOf:)and a MIME type derived from the path extension;UIImageis re-encoded viapngData()and served asimage/png. The PNG bytes are cached perUIImage(NSCache) so repeat webview requests don't re-encode on the main thread.@objc(localResources)) is typed[String: NSObject]and accepts bothNSURLandUIImagevalues; anything else is dropped. Note: the ObjC property's generic type widened fromNSDictionary<NSString *, NSURL *> *toNSDictionary<NSString *, NSObject *> *. Setter assignments and dictionary literals continue to work; ObjC code that previously assigned the getter into anNSDictionary<NSString *, NSURL *> *local will now see an "incompatible pointer types" warning that's resolved by retyping the local or casting.SWLocalResourcesViewController(debug view) previews both URL andUIImageentries.URL. Asset catalog support is scoped to images, which covers the Pylon ask.trailing_closurelint violation inConfigLogic.swift:310while in the area.Tests
Extends
LocalFileSchemeHandlerTests:URLregistered through the unified dictionary still loads bytes and a MIME type.UIImageregistered directly is served asimage/pngwith bytes matchingpngData().Checklist
CHANGELOG.mdfor any breaking changes, enhancements, or bug fixes.swiftlintin the main directory and fixed any issues.🤖 Generated with Claude Code
Greptile Summary
Generalises
SuperwallOptions.localResourcesfrom[String: URL]to[String: AssetResource], addingUIImageconformance so customers can register asset-catalog Image Sets without needing a file URL. The ObjC bridge, PNG caching, debug preview, and tests are all cleanly handled, and all issues raised in previous review rounds appear to have been addressed in this iteration.Confidence Score: 5/5
PR is safe to merge; no P0/P1 issues found in the current implementation.
The change is well-scoped and backwards-compatible. URL call sites continue to work unchanged, the ObjC bridge uses the established @available(swift, obsoleted:) pattern correctly, the manual encode(to:) already excludes localResources so Encodable synthesis is not a concern, NSCache is thread-safe and UIImage.pngData() is documented as thread-safe, and both new code paths have test coverage. All issues flagged in prior review threads have been resolved in this iteration.
No files require special attention.
Important Files Changed
Sequence Diagram
sequenceDiagram participant App participant SuperwallOptions participant WKWebView participant LocalFileSchemeHandler participant NSCache App->>SuperwallOptions: localResources["logo"] = UIImage(named:) App->>SuperwallOptions: localResources["hero"] = Bundle.main.url(...) WKWebView->>LocalFileSchemeHandler: webView(_:start:) [swlocal://logo] LocalFileSchemeHandler->>SuperwallOptions: options.localResources["logo"] SuperwallOptions-->>LocalFileSchemeHandler: UIImage instance LocalFileSchemeHandler->>NSCache: object(forKey: image) alt Cache hit NSCache-->>LocalFileSchemeHandler: NSData (PNG bytes) else Cache miss LocalFileSchemeHandler->>LocalFileSchemeHandler: image.pngData() LocalFileSchemeHandler->>NSCache: setObject(data, forKey: image) end LocalFileSchemeHandler-->>WKWebView: URLResponse + PNG data (image/png) WKWebView->>LocalFileSchemeHandler: webView(_:start:) [swlocal://hero] LocalFileSchemeHandler->>SuperwallOptions: options.localResources["hero"] SuperwallOptions-->>LocalFileSchemeHandler: URL (file path) LocalFileSchemeHandler->>LocalFileSchemeHandler: Data(contentsOf: url) LocalFileSchemeHandler-->>WKWebView: URLResponse + file data (MIME from extension)Reviews (6): Last reviewed commit: "Promote image PNG cache to static for cr..." | Re-trigger Greptile