ReviewOS

craft-native/craft

craft.d.ts promises 33 methods window.craft does not have — 16 of them work on Android and TypeError on iOS

#140
Closed chris opened this 9 days ago ·
9 days ago

craft.d.ts declares window.craft as CraftBridge, an interface with 78 direct methods. Thirty-three of them do not exist on the object iOS injects, and seventeen do not exist on either platform's.

They type-check. They throw TypeError at runtime.

// compiles clean; works on Android; TypeError on iOS
const tag = await window.craft.scanNFC()

Absent from iOS, present on Android (16)

scanNFC · scanQRCode · takeScreenshot · startAudioRecording · stopAudioRecording · startVideoRecording · pickFile · downloadFile · saveFile · startMotionUpdates · stopMotionUpdates · signInWithGoogle · toggleFlashlight · onAppStateChange · offAppStateChange · offNetworkChange

These are the sharpest, because the same source compiles and runs on one platform and not the other, with nothing in between to warn you.

Absent from both (17)

getContacts · addContact · getCalendarEvents · createCalendarEvent · deleteCalendarEvent · scheduleNotification · cancelNotification · cancelAllNotifications · getPendingNotifications · restorePurchases · signInWithApple · startBluetoothScan · stopBluetoothScan · requestHealthAuthorization · getHealthData · requestFitnessAuthorization · getFitnessData

For several of these the capability exists under a namespace — craft.contacts.…, craft.notifications.… — so what is wrong is the declaration, not the bridge. That still means the published types describe a shape neither runtime has.

What this is not

The actions work. Both bridges expose a generic escape hatch — craft._invoke(action, payload) on iOS (CraftApp.swift:2543) and craft.invoke('namespace.action', params) in craft-bridge.js:297 — and the dispatcher arms behind these names are implemented, several of them now in Zig. So this is a missing named convenience method and a type declaration that promises one, not a missing capability.

I checked for that hatch before writing this issue, because "unreachable" was my first reading and it was wrong.

Why nothing caught it

ios_conformance_test.zig checks one direction only, and says so:

every action the page can call is one the spec handles — The ota* check. Five methods in the injected JavaScript posted actions the switch never handled…

The reverse — a handled action with no method, or a declared method with no implementation on either side — has never been checked. bridge_mobile_motion.zig found one instance by hand and wrote it down:

Worth knowing while reading any of that: window.craft.startMotionUpdates does not exist in the injected iOS JavaScript. … So the README example and the test page's two buttons are TypeErrors on the legacy path … That is a second pre-existing bug

One module noticed, recorded it in its own header, and nothing else could see it. Same shape as craftShortcut in bridge_mobile_shortcuts.zig (#127) and the twelve gate claims fixed in #132. It is thirty-three, not one.

The check that would hold it

Three sets, all scannable from source, and the same shape as the event-channel checks in #128:

  1. method names on CraftBridge in craft.d.ts
  2. name: function keys on iOS's injected window.craft
  3. the same on Android's

Every declared method should exist on both, with a recorded-and-checked table for the deliberate exceptions — platform-only capabilities like craft.siri.* and craft.watch.* are legitimate and should be rows with reasons rather than silent absences.

Fixing the 33 is a separate question from checking them, and probably a mix of adding thin wrappers over _invoke and correcting declarations that were never true.

9 days ago

Fixed in 0e24305. Both shipped bridges now expose every direct method declared by CraftBridge; existing native actions and namespaced implementations are wrapped, and platform-only APIs reject explicitly instead of throwing TypeError. The conformance check now requires direct methods on both iOS and Android and also caught five additional nested-only mismatches beyond the original 33. CI, SBOM, Binary Size, and Benchmark are all green.

Sign in to comment on this issue.