Background sync
There are two separate background paths:
- BLE background measurement delivery, coordinated by BTProvider and its durable result queue.
- Android Health Connect background import, scheduled by WorkManager and entered through a headless JS task.
They have different permissions, lifecycle rules, and failure modes.
BLE result queue
Enable BLE background delivery on BTProvider with backgroundSync:
<BTProvider
bleManager={bleManager}
acceptedDevices={acceptedDevices}
backgroundSync={{
enabled: true,
storage: {
getItem: (key) => storage.getItem(key),
setItem: (key, value) => storage.setItem(key, value),
},
restoreStateIdentifier: "com.example.app.bluetooth",
onResult: async (result) => {
await uploadMeasurement(result);
},
maxRetries: 3,
retryBackoffMs: 30_000,
}}
/>
The storage adapter must be durable across process death. AsyncStorage, SQLite, or a KV store can be used when the app supplies the required asynchronous methods. Do not use an in-memory object.
The queue writes a result before delivery and removes it only after onResult resolves. This is at-least-once delivery: a process death between those operations can deliver the same result again. Use the stable result.id as the idempotency key in the backend.
Each queued item includes:
- id;
- queuedAt;
- attempt;
- the original deviceData and decoded measurement.
Failures retry with exponential backoff. The default is three attempts and a 30-second base delay. After the final failed attempt the item remains as exhausted, rather than being silently discarded. A later foreground/background flush can explicitly include and reset exhausted items.
iOS CoreBluetooth restoration
Create the BLE manager once, outside React renders:
import { createBackgroundBleManager } from "@ovok/native";
export const bleManager = createBackgroundBleManager({
restoreStateIdentifier: "com.example.app.bluetooth",
});
Keep the restoration identifier stable for the lifetime of the app. Configure react-native-ble-plx background central mode in the Expo plugin. When iOS restores the manager, the provider rehydrates restored peripherals and flushes the durable queue.
When background scanning is enabled, iOS requires service UUIDs. Built-in declarations contribute their known UUIDs; custom devices must add their main service UUIDs through the declaration or backgroundSync.serviceUUIDs.
iOS decides when restoration and background delivery run. A restoration identifier is not a guarantee of continuous execution, and HealthKit locked-device behavior is separate from BLE restoration.
Android foreground service
Android can stop a background JS process. For a BLE scan that must remain alive while the app is backgrounded, start the native foreground service while the provider-owned scan is active:
import {
startAndroidBluetoothForegroundService,
stopAndroidBluetoothForegroundService,
} from "@ovok/native";
await startAndroidBluetoothForegroundService({
notificationTitle: "Bluetooth monitoring",
notificationBody: "Listening for health measurements",
});
// Stop when the monitoring session ends.
await stopAndroidBluetoothForegroundService();
The service needs the Android foreground-service permissions in the native build. The optional requestBatteryOptimizationExemption flag opens explicit system consent; it is not enabled by default and should be explained to the user.
The native service does not serialize an authenticated Ovok client. The app remains responsible for restoring authentication and uploading queued results.
Android Health Connect scheduling
Health Connect background import is not the BLE service. Register a headless task at app entry:
import {
registerAndroidHealthConnectBackgroundTask,
runAndroidHealthConnectSync,
} from "@ovok/native";
registerAndroidHealthConnectBackgroundTask(async () => {
const client = await restoreAuthenticatedClient();
await runAndroidHealthConnectSync({
client,
patientId: await restorePatientId(),
dataToSync: androidDataToSync,
onError: reportSyncError,
});
});
Then schedule WorkManager:
import { scheduleAndroidHealthConnectSync } from "@ovok/native";
await scheduleAndroidHealthConnectSync({
intervalMinutes: 15,
taskName: "OvokHealthConnectBackgroundSync",
});
The minimum supported WorkManager interval is 15 minutes. The task name must match the registered name. The task must restore the app's client, patient identity, and mapping; the SDK cannot persist an auth token or React tree for a headless process.
Request Health Connect's background read permission by setting backgroundAccess on DataSync.AndroidHealthConnectAuthorizationProvider. Treat authorized, refused, error, and shouldRequest as different states in the UI.
Background checklist
- Native BLE and foreground-service permissions are in the built binary.
- The BLE manager is created once and has a stable restoration identifier.
- The queue uses durable storage and the upload endpoint deduplicates by result.id.
- onResult resolves only after the server accepts the result.
- iOS background scans have service UUID filters.
- Android's foreground service is started/stopped with the monitoring session.
- The Health Connect headless task is registered from the app entry point.
- The headless task restores auth and patient state itself.
- WorkManager scheduling and OS battery policy are documented to users.