TITLE:
iOS companion: device returns from getConnectedDevices but reports NOT_PAIRED, getApplicationInfo says NOT_INSTALLED, and every Communications.transmit() hits onError
BOARD:
Connect IQ > Connect IQ App Development Discussion
SETUP
Watch: Forerunner 245 Music (fr245m), firmware current
SDK: Connect IQ 9.2.0 on Windows, building with the monkeyc CLI (the VS Code
extension is unusable for me - CIQQA-3791)
Watch app: device app (type="watch-app"), manifest declares Positioning and
Communications permissions and lists fr245m
Companion app: iOS, React Native / Expo, using the community wrapper
react-native-connect-iq-mobile-sdk over the official iOS Mobile SDK
Distribution: the watch app is uploaded to the Connect IQ Store as a beta app
and installed on the watch through the Connect IQ mobile app (not sideloaded).
The manifest appId and the store appId match.
WHAT WORKS
The AppDelegate openURL hook is wired up and confirmed firing. Garmin Connect
returns a well-formed callback, for example:
myscheme://device-select-resp?ciqApp=Connect&ciqBundle=com.garmin.connect.mobile
&ciqSdkVersion=10000&d0ID=<uuid>&d0Model=Forerunner%20245%20Music
&d0Name=Forerunner%20245%20Music&d0PartNumber=006-B3077-00
getConnectedDevices() returns exactly one device with the correct friendlyName
("Forerunner 245 Music").
setDevice() on that returned object succeeds.
registerForAppMessages(appId) resolves without error.
The watch and phone are paired and connected normally. Garmin Connect Mobile and
the Connect IQ app both show the watch as Connected, syncing works, and the
watch's "Find My Phone" successfully makes the phone ring - so the BLE link and
the device-level pairing are clearly healthy.
WHAT FAILS
1. The device status callback reports NOT_PAIRED for that device ID (the same
UUID that came back in the d0ID param).
2. getApplicationInfo(appId) reports {"status":"NOT_INSTALLED","version":"0"},
even though the app is installed via the store and visibly runs on the watch.
3. On the watch, Communications.transmit(payload, null, listener) always lands in
ConnectionListener.onError(). Confirmed on-device by drawing the result on the
watch screen, since System.println isn't visible on real hardware.
4. No message is ever delivered to the companion app's message listener.
One other oddity: in the Connect IQ mobile app, "My Device Apps" showed a count
of 1 for this watch while rendering an empty list.
CONTROL TEST - GARMIN'S OWN COMM SAMPLE FAILS THE SAME WAY
To check whether my watch code was at fault, I built the SDK's own Comm sample
(added fr245m to its products list, no other changes), sideloaded it, and pointed
my companion app's registerForAppMessages at the Comm sample's own app id
(a3421feed289106a538cb9547ab12095).
I sent "hello world" from the Comm sample's Send menu on the watch. Nothing
arrived at the companion app. getApplicationInfo for the Comm sample's app id
also returns NOT_INSTALLED, and the device still reports NOT_PAIRED.
So Garmin's own reference watch app behaves identically to mine, which seems to
rule out my watch-side code.
I can't run Garmin's iOS example companion app to complete the isolation - no Mac
or Xcode available - which is why I'm asking here.
ALREADY RULED OUT
- Registering for app messages with an empty-string appId - no effect
- Sideload vs store install - same behavior either way
- appId mismatch - manifest appId and store appId now match; also tried the phone
side registering an older appId to match an older build
- App id format - tried both a dashed GUID and a conventional undashed 32-hex id
(including Garmin's own sample id); identical results
- Handing setDevice() a hand-constructed device object vs. the object returned by
getConnectedDevices() - same result either way. Note the returned object's
status field comes back undefined through the wrapper.
- Call ordering - re-registered the device-event and app-message listeners
immediately after setDevice(), in case registerForDeviceEvents was being called
before a device existed. No change.
- Companion app backgrounded - the app is open and in the foreground during tests
- Bluetooth range or connection - the watch is connected and syncing throughout
QUESTIONS
What causes a device to report NOT_PAIRED when it was just returned by the device
selection flow, and is that status the reason transmit() is refused? My working
assumption is that everything else here (NOT_INSTALLED, the refused transmits,
the empty app list) is downstream of that one status, since an invalid device
can't be queried about apps or routed messages.
Is there a registration step between setDevice() and registerForAppMessages()
that I'm missing on iOS?
Is getApplicationInfo expected to return NOT_INSTALLED for a beta store app that
is installed on the device? If beta apps stay permanently "pending", does that
block app-message routing?
Happy to provide logs or put together a minimal repro.