iOS app’s first-time experience showed “No walkthroughs are available on this
platform yet.” in the FlowChooser empty state. Android (and JVM/desktop)
worked fine — every flow surfaced as expected. WASM was unaffected because it
fetches flow JSONs over HTTP rather than from the classpath.
Flow JSON files lived at shared/src/commonMain/resources/flows/<id>.json.
That path is on the JVM/Android classpath but Kotlin/Native iOS frameworks
do not carry JVM resources. NSBundle.mainBundle.pathForResource(...) in
shared/src/iosMain/.../FeatureLoader.ios.kt::readClasspathResource returned
nil for every flow id, so FlowRegistry.loadAll() populated an empty map,
and the chooser fell through to its empty-state branch.
The wider class of bug: anything that lands in shared/src/commonMain/resources/
and depends on classpath access at runtime is invisible on iOS.
composeApp/src/commonMain/composeResources/files/flows/
so Compose Multiplatform’s resource pipeline carries them into the iOS app
bundle via embedAndSignAppleFrameworkForXcode.composeApp/.../walkthrough/ComposeFlowLoader.kt (composeFlowLoader)
that reads via Res.readBytes("files/flows/$id.json") — works uniformly on
Android, iOS, JVM/desktop and WASM.FlowRegistry’s Koin binding in composeModule to inject
::composeFlowLoader, replacing the appModule default that pointed at
the now-stub flowLoader in shared/.internal on flowJson so the new loader (in composeApp) can use
the same lenient Json configuration as the rest of the flow code.Two regression guards added in FlowRegistryTest:
everyBundledFlowIdHasAComposeResourcesFile — fails the JVM test suite
if a bundledFlowIds entry has no JSON file at the iOS-bundling path.noStrayFlowsLeftInSharedResources — fails if any future commit re-adds
shared/src/commonMain/resources/flows/*.json. That path silently works
on Android/JVM and silently breaks iOS, which is exactly the trap that
produced this bug.General rule: if a non-composeResources resource needs to ship in the
mobile apps, it doesn’t ship on iOS. New runtime-loaded resources go under
composeApp/src/commonMain/composeResources/, not under
shared/src/commonMain/resources/. The shared resources tree is JVM-only —
fine for tests, fine for the server, not fine for KMP-mobile-loaded data.