Custom .symbolset images fail to resolve on Play builds with automatic integrity protection (AssetManager.list-based resourcesIndex)
Nobody has claimed this yet.
- Dominant language
- Swift
- Stars
- 330
- Forks
- 76
- Avg merge
- 3h 6m
- Merged PRs (30d)
- 1
Description
Summary
Custom .symbolset images (Image(systemName:) resolving to asset-catalog symbolsets) fail to render on builds delivered by Google Play when the Play Console's automatic integrity protection is enabled. Built-in Material-mapped symbols render fine; every custom symbolset falls back to the warning-triangle placeholder with:
W skip.ui.SkipUI: Unable to find system image named: headphones
W skip.ui.SkipUI: Unable to find system image named: flame.fill
The same AAB's artifacts work correctly when installed directly (universal APK or bundletool-built splits), on both Android 14 and Android 16. Only the Play-processed build fails.
Environment
- skip 1.8.9, skip-ui 1.51.3, skip-foundation 1.4.0 (Skip Lite / transpiled)
- Release build: R8 + resource shrinking on,
-keep class skip.** { *; }etc. from the template proguard rules - Observed on a Pixel 8a (Android 16) with a Play internal-testing install; reproduced nowhere else
Root cause analysis
Image(systemName:) resolves custom symbols via symbolResourceURL(name:bundle:) → assetContentsURLs(name:bundle:) → Bundle.resourcesIndex (SkipFoundation Bundle.swift), which builds its index by recursively walking APK assets with AssetManager.list().
Play's automatic integrity protection injects an anti-tamper wrapper into the served classes.dex (in our case +26 KB vs. the uploaded AAB, different CRC; all other entries byte-identical). Under that wrapper, the AssetManager.list()-based enumeration no longer finds the symbolset entries, so resourcesIndex misses them and every custom symbolset lookup fails. The .symbolset SVG files themselves ARE present in the delivered base.apk (verified by pulling the APKs off the device with adb shell pm path + unzip -lv).
Built-in symbol names are unaffected because they map to compiled Compose vectors and never touch the assets index.
Reproduction
- App with a custom
.symbolsetinModule.xcassetsreferenced viaImage(systemName:). - Upload a release AAB to any Play track with Play Console → App integrity → automatic integrity protection ON (default-checked for new apps at first release).
- Install via Play on a device → custom symbols render as the missing-icon placeholder; logcat shows
Unable to find system image named: <name>. - Same AAB installed locally (universal APK or
bundletool build-apks+install-apks) renders correctly — the issue only manifests on Play-served builds, so it is invisible in all local/emulator testing.
Side observation confirming the wrapper: side-loading the Play-delivered APKs onto an emulator shows the injected licensing dialog ("Check that Google Play is enabled…") instead of launching.
Workaround
Disable automatic integrity protection and roll out a new release (the toggle only affects newly processed releases). Verified fixed: with protection off, the delivered classes.dex CRC matches the uploaded AAB and all symbolsets render.
Suggested direction
A build-time-generated resource index (e.g., the transpiler emitting the Module.xcassets entry list into generated code, with the AssetManager.list() walk as fallback) would make symbol resolution independent of runtime asset enumeration, and Skip apps could then coexist with Play's integrity protection.
Happy to provide the pulled-APK diffs or test candidate fixes on our app.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in SkipFoundation's Bundle.swift and trace symbolResourceURL(name:bundle:) through assetContentsURLs(name:bundle:) to Bundle.resourcesIndex. Compare the AssetManager.list()-based index behavior with the generated resource information suggested in the issue. Done means custom .symbolset images resolve correctly in Play-served builds with automatic integrity protection enabled, while existing local installs continue to work.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, swift
- Domain
- mobile-dev
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100