iPhone accepts BLE HID keyboard base keys but strips Shift from composite mouse+keyboard device

I’m debugging a custom BLE HID device on iPhone. It is a composite HID mouse + keyboard dongle.

Setup:

  • Hardware: Seeed XIAO nRF52840
  • Firmware: Adafruit Bluefruit Arduino / BLEHidAdafruit
  • BLE HID report map: stock Adafruit composite HID with keyboard, consumer, and mouse reports
  • GAP/advertising appearance: HID_MOUSE
  • iOS adopts the device as an AssistiveTouch pointer
  • Mouse movement and clicks work correctly

Keyboard symptom:

  • Lowercase/unshifted characters type correctly.
  • Shifted characters lose the Shift modifier during text input:
    - A -> a
    - T -> t
    - DoorDash -> doordash
    - ! -> 1
    - @ -> 2
    - # -> 3
    - { -> [
    - } -> ]

Confirmed:

  • The iOS app sends the exact intended string to the dongle.
  • Firmware receives the exact string.
  • Firmware computes and sends the expected HID modifier/keycode:
    • A sends modifier 0x02 + HID_KEY_A
    • ! sends modifier 0x02 + HID_KEY_1
  • A lone isolated "A" still lands as "a", so this does not appear to be a timing or repeated-key issue.
  • Cmd+Space works from the same HID keyboard report path and opens Spotlight.
  • Full Keyboard Access is off.
  • Turning AssistiveTouch off does not fix it.
  • The iPhone never shows "Hardware Keyboard" settings for this device, even when searching Settings.

Question: Is there a documented distinction on iOS between accepting BLE HID keyboard reports for global shortcuts, such as Cmd+Space, and admitting the same device as a full Hardware Keyboard for text composition?

In particular:

  • Does the absence of Hardware Keyboard settings mean iOS has not classified the device as a real external keyboard?
  • Can a composite BLE HID device advertised as HID_MOUSE be accepted for pointer input but have Shift ignored for text input?
  • Does iOS require a different GAP appearance, HID report-map structure, report ordering, or separate keyboard identity for Shift/modifier text composition to work?
  • Is there a recommended way to build a BLE HID device that preserves AssistiveTouch pointer behavior while also being treated as a full external keyboard?

I’m debugging a custom BLE HID device on iPhone. It is a composite HID mouse + keyboard dongle.

So, the first thing I would do here is pair the device with macOS and see what happens there. One of two things will happen in that testing:

  1. (most likely) You see exactly the same failure on macOS-> macOS has much much better tools and access to HID and event systems, making it easier to investigate and debug.

  2. (less likely) It work fine on macOS-> You can now file a bug saying "my keyboard work fine on macOS but fails on iOS", which is a much more "actionable" issue than a HID device simply behaving incorrectly.

Basically, I'd expect any macOS keyboard to work on iOS, so it's easier to focus on macOS first before you worry about iOS. Critically, on the tools side, you can use our HID APIs to see exactly what the machine is receiving and CGEventTap to see exactly how the event system interpreted your HID event(s), both for your accessory and any other accessory you want to test with. At that point, debugging is mostly a matter of comparing behavior and correcting any divergence.

Does iOS require a different GAP appearance, HID report-map structure, report ordering, or separate keyboard identity for Shift/modifier text composition to work?

...

Does iOS require a different GAP appearance, HID report-map structure, report ordering, or separate keyboard identity for Shift/modifier text composition to work?

Our exact requirements for all of this are documented in "Accessory Design Guidelines for Apple Devices", which is linked to off of our Accessories landing page.

Does the absence of Hardware Keyboard settings mean iOS has not classified the device as a real external keyboard?

I'm not certain of the exact criteria but my guess is:

  1. You need to conform to the guidelines above to be listed as "Hardware Keyboard".

  2. The system will still accept events from "other" configuration, most likely to support things like barcode scanners[1].

However, I don't think that alone explains what shift itself is failing. My guess is that this is an issue with the actual event you're sending, but I don't know enough about exactly what's wrong with the event.

Is there a recommended way to build a BLE HID device that preserves AssistiveTouch pointer behavior while also being treated as a full external keyboard?

Our design guidelines actively encourage combination keyboard + trackpad accessories, ro example p.100 (and other places):

"Keyboards should be integrated with Trackpads (page 106) when possible to provide an enhanced user experience."

They don't offer any additional guidance on these, so I suspect it should "just work", assuming the reports are all structured correctly.

[1] Many barcode scanner present themselves to the system as "keyboards" which then work by "typing" out whatever they scan. This is deeply weird, but it makes them compatible with the broadest possible set of use cases with the minimal development effort for users.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

iPhone accepts BLE HID keyboard base keys but strips Shift from composite mouse+keyboard device
 
 
Q