skiptools / skiptools/skip-ui

Custom .symbolset images fail to resolve on Play builds with automatic integrity protection (AssetManager.list-based resourcesIndex)

Open
#497 0 comments 0 reactions 0 assignees View on GitHub

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

  1. App with a custom .symbolset in Module.xcassets referenced via Image(systemName:).
  2. 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).
  3. Install via Play on a device → custom symbols render as the missing-icon placeholder; logcat shows Unable to find system image named: <name>.
  4. 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.