# Premium APIs — `window.webbleIOS`

The standard polyfill at `navigator.bluetooth` is free and App-Store-gated: anyone with the WebBLE extension gets the full W3C surface, portable, cross-browser-compatible.

Beyond that, WebBLE exposes iOS-only capabilities under a vendor-prefixed global: **`window.webbleIOS`**. These capabilities are deliberately not part of the W3C spec — they extend what the web can do on iPhone beyond what any other browser offers.

Feature-detect before use:

```js
if (!('webbleIOS' in window)) {
  // standard surface only; fall back or upsell the companion app
  return;
}
```

## The surface

```ts
interface WebBLEIOS {
  readonly peripheral: WebBLEPeripheralManager;
  readonly backgroundSync: WebBLEBackgroundSync;
  getCapabilities(): Promise<WebBLECapabilities>;
}
```

## Capabilities

### Peripheral mode — `window.webbleIOS.peripheral`

Turn the iPhone into a GATT peripheral advertising custom services. Scanning centrals (other phones, laptops, sensor tags) discover and connect to it like any dedicated device. No other browser exposes this.

Core methods:

- `addService(definition)` — register a GATT service with characteristics and their properties (`read`, `write`, `notify`, …).
- `startAdvertising(options?)` — begin advertising; options include `localName` and `serviceUUIDs`.
- `stopAdvertising()` — stop.

Events: `onwriterequest`, `onsubscriptionchange`, `onconnectionstatechange`, `onadvertisingstatechange`, `onnotificationready`.

See [recipes: peripheral chat](recipes.md#peripheral-chat) for a runnable example.

---

### Background sync — `window.webbleIOS.backgroundSync`

Continue BLE work while Safari is backgrounded or the phone is locked. Requires the WebBLE companion app to be installed and running.

Three first-class registration types:

#### `requestBackgroundConnection({ deviceId })`

Keeps a previously-paired device connected while Safari is backgrounded. No OS notification is shown — the connection is just maintained so that when the user returns, the device is still live.

```js
const reg = await window.webbleIOS.backgroundSync.requestBackgroundConnection({
  deviceId: device.id
});
```

#### `registerCharacteristicNotifications(options)` — *the "notifications" premium API*

Deliver an iOS notification when a GATT characteristic value changes in the background, with a template-interpolated title/body and an optional reply action.

```js
await window.webbleIOS.backgroundSync.registerCharacteristicNotifications({
  deviceId: device.id,
  serviceUUID: 'heart_rate',
  characteristicUUID: 'heart_rate_measurement',
  condition: { decode: 'uint8', operator: 'gt', threshold: 150 },
  template: {
    title: 'High heart rate',
    body: '{{deviceName}} is at {{value.utf8}} bpm',
    url: 'https://example.com/alert'
  },
  cooldownSeconds: 30,
  replyAction: { actionTitle: 'Ack', placeholder: 'Reply…' }
});
```

Supported decoders: `uint8`, `int16be`, `int16le`, `int32be`, `float32le`, `float32be`.
Supported operators: `gt`, `lt`, `gte`, `lte`, `eq`, `neq`, `changed`, `always`.
Template placeholders: `{{device.name}}`, `{{device.id}}`, `{{value.hex}}`, `{{value.utf8}}`, `{{value.int16be}}`, `{{value.int32be}}`, `{{timestamp}}`.

#### `registerBeaconScanning(options)` — *the "beacons" premium API*

Fire an iOS notification when a matching BLE advertisement is seen while backgrounded. iOS hardware-filters on service UUIDs; `namePrefix` is applied in software.

```js
await window.webbleIOS.backgroundSync.registerBeaconScanning({
  filters: [{ services: ['battery_service'], namePrefix: 'Acme' }],
  cooldownSeconds: 30,
  template: { title: 'Acme beacon in range', body: 'at {{timestamp}}' }
});
```

**iOS platform note:** beacon delivery is optimized for quick catch-up rather than guaranteed fixed real-time discovery while the app is backgrounded.

#### Lifecycle

- `requestPermission()` — explicitly request notification permission ahead of registering.
- `getRegistrations()` — list active registrations for this origin.
- `unregister(id)` / `update(id, partialTemplate)` — teardown / modify.

Every registration is persisted in the companion app; it survives Safari closing, the phone rebooting, and the extension process restarting.

---

### Live Activities

Live Activities (Lock Screen / Dynamic Island) are **not** a direct JS API. The companion app manages them automatically while running in IPC-relay mode: when the page has one or more active background-sync registrations, the companion app drives an ActivityKit session reflecting keep-alive connections, characteristic notification alerts, and beacon scans.

No opt-in is required from the page; no opt-out surface is exposed today. If you need programmatic control over the Live Activity, open an issue on the GitHub repository.

---

### `getCapabilities()`

Returns a bag of runtime capability flags negotiated with the companion app — use it to branch between standalone mode (Safari owns BLE) and IPC-relay mode (companion app owns BLE, background work is allowed).

```js
const caps = await window.webbleIOS.getCapabilities();
```

The exact shape is forward-compatible by design; treat unknown keys as additive.

## Source of truth

- [`src/webble-ios/index.ts`](https://github.com/wklm/ioswebble-sdk/blob/main/src/webble-ios/index.ts) — surface construction
- [`src/webble/api/webble-peripheral.ts`](https://github.com/wklm/ioswebble-sdk/blob/main/src/webble/api/webble-peripheral.ts) — peripheral manager
- [`src/types/background-sync.ts`](https://github.com/wklm/ioswebble-sdk/blob/main/src/types/background-sync.ts) — background-sync types
