ReviewOS

craft-native/craft

Four page event subscriptions have no emitter: craftShortcut, craftSiriShortcut, craftOTAProgress, craftOTAStatus

#127
Closed glennmichael123 opened this 8 days ago ·
8 days ago

Four events the injected JavaScript subscribes to are dispatched by nothing in this repository. The callback a page registers against each is stored, retained, and never called.

This is the ota* bug on the other channel. The action scan in packages/zig/test/ios_conformance_test.zig found five ota* methods whose promises could never settle; the same file's new event scans find four subscriptions whose callbacks can never fire. Both halves of CraftApp.swift disagree with each other, in the same way, for the same reason: nothing checked.

The four

eventsubscriberwhy nothing dispatches it
craftShortcutcraft.shortcuts.onShortcut — CraftApp.swift:2226No template implements application(_:performActionFor:completionHandler:) or a scene-delegate equivalent. setShortcuts really does install the home-screen items; tapping one launches the app and stops there.
craftSiriShortcutcraft.siri.onInvoke — CraftApp.swift:2417donateSiriShortcut donates an NSUserActivity typed .<action> (:5027). The only onContinueUserActivity in the template is bound to NSUserActivityTypeBrowsingWeb (:81) and routes to DeepLinkManager, so invoking a donated shortcut relaunches the app and is dropped.
craftOTAProgresscraft.ota.onProgress — CraftApp.swift:2517The OTA surface is unavailable: checkForUpdate, downloadUpdate, applyUpdate and rollback all reject through _unavailable.
craftOTAStatuscraft.ota.onStatusChange — CraftApp.swift:2521Same.

Severity, honestly

Quieter than the ota* promises, and for that reason likelier to survive. A promise that never settles hangs an await and eventually shows up as a stuck screen. A subscription that never fires produces no error, no timeout and no log — the page behaves exactly as it would if the device had nothing to report. craft.ota.onProgress(cb) and a working OTA implementation with no update available are indistinguishable from the page's side.

The craftShortcut half was already known and written down, in bridge_mobile_shortcuts.zig's header — prose, in one module, that nothing verified and no other module could see. That is the same shape as the three deferral reasons that had rotted before this migration started checking them.

What is not the fix

bridge_mobile_shortcuts.zig and bridge_mobile_siri.zig serve their actions correctly; the missing half is an app-delegate callback in the Swift template, not anything Zig can add. Two of the four are also a product question rather than a bug to close — OTA has no implementation at all, so onProgress has nothing to report even in principle, and the honest fix there is to make the two subscription methods refuse the way the four promise-returning ones already do.

Meanwhile

All four are recorded in dead_subscriptions in packages/zig/test/ios_conformance_test.zig with the anti-rot guards the deferral table uses: a row whose event stops being subscribed fails the build, and so does a row that grows an emitter. A fifth dead subscription — a new one — fails the build immediately.


Update: the Android bridge makes three of these worse, and adds a fifth

The original scan covered .swift, .zig, .ts and .js and never opened a .kt.template. Reading CraftBridge.kt changes the picture — in the direction of more dead surface, not less.

Android's injected JS subscribes to seven craft* events; its Kotlin dispatches eleven. Comparing the two:

eventsubscribed ondispatched onverdict
craftShortcutiOS and Androidneitherdead on both platforms
craftOTAProgressiOS and Androidneitherdead on both platforms
craftOTAStatusiOS and Androidneitherdead on both platforms
craftSiriShortcutiOSneitherdead; iOS-only surface
craftARPlaneiOS and AndroidiOS onlynew — dead on Android

So three of the four are not an iOS oversight; they are a gap in the shared JavaScript that both bridges inject, which is why both platforms register a listener and neither ever fires it. That also means fixing them is not two separate platform jobs but one contract question asked twice.

craftARPlane is the fifth, and a different shape: bridge_mobile_ar.zig dispatches it on iOS, so a page using AR plane detection works there and silently receives nothing on Android. That one is a genuine platform divergence rather than a surface nobody finished.

For completeness, Android dispatches two events iOS does not — craftVoiceAction and craftAppStateChange — both of which were missing or wrong in the SDK's type map until #135.

What this does not change

The dead_subscriptions table in ios_conformance_test.zig is still correct as written: it records what the iOS spec subscribes to without dispatching, and all four rows hold. What it cannot see is the Android side, and nothing checks that yet — an Android conformance ratchet is the missing piece, and it would have found craftARPlane on its own.

8 days ago

Fixed in c37c221. Native shortcut and Siri events now queue until the page bridge is ready and emit craftShortcut/craftSiriShortcut on iOS; Android shortcut intents are delivered on cold and warm launch. Unsupported OTA subscription paths now fail explicitly instead of installing dead listeners. Verified by the full Zig suite, mobile builder tests, both mobile package builds, generated Swift typechecking, and pickier. Required workflows are green: CI https://github.com/craft-native/craft/actions/runs/34686989260, SBOM https://github.com/craft-native/craft/actions/runs/34686989210, Binary Size https://github.com/craft-native/craft/actions/runs/34686989229, Benchmark https://github.com/craft-native/craft/actions/runs/34686989232.

Sign in to comment on this issue.