iOS SDK
Log product telemetry
Attach structured product events to the active customer without blocking the app on delivery.
Record a connection failure
BTX.log(...) accepts an event immediately and sends it later. The static facade returns an enqueue disposition, not a network-delivery result.
Record the connection stage, firmware, and failure reason from your companion app’s connection manager. The example uses sample Mithril Wearables state.
BTX.log(
"device_connection_failed",
properties: [
"deviceId": "ring_123",
"deviceModel": "Mithril One",
"firmwareVersion": "2.3.7",
"transport": "bluetooth",
"connectionStage": "service_discovery",
"errorCode": "connection_timeout",
"retryCount": 2
]
)The SDK buffers logs until configuration, identity, and runtime transport are ready.
Use structured properties
Properties can contain strings, integers, doubles, booleans, nil, arrays, dictionaries keyed by String, or explicit BTXJSONValue values.
- Use a stable event name such as
device_connection_failed. - Prefer product identifiers and bounded values over free-form dumps.
- Do not include passwords, access tokens, or server credentials.
- Keep property shapes consistent so events remain comparable.
Use automatic runtime context
When logs are enabled, BTX records one canonical app.snapshot event after the runtime starts. It includes privacy-safe app, operating system, device family, architecture, simulator, and SDK version context.
The SDK intentionally excludes advertising identifiers, identifierForVendor, serial numbers, the user-assigned device name, and other stable personal device identifiers.
Report device health
Report battery level and health, charging state, firmware, and product-specific
attributes from your device integration. Enable .logs and identify the customer
before reporting. Call this helper on the main actor when hardware state changes.
The manufacturer, model, and namespace below are sample Mithril Wearables values.
import Foundation
import BTXClientKit
@MainActor
func reportDeviceHealth(
peripheralID: UUID,
hardwareID: String,
firmwareVersion: String,
batteryPercent: Double?,
batteryHealthPercent: Double?,
isCharging: Bool?,
hardwareVersion: String? = nil,
attributes: [String: BTXJSONValue] = [:]
) {
BTX.deviceSnapshots.refresh(connectionKey: peripheralID.uuidString) {
BTXDeviceSnapshot(
identifier: .vendorHardwareID(
hardwareID,
namespace: "mithril.hardware_id"
),
additionalIdentifiers: [
.coreBluetoothPeripheralUUID(peripheralID)
],
manufacturer: "Mithril Wearables",
category: "wearable",
model: "Mithril One",
firmwareVersion: firmwareVersion,
hardwareVersion: hardwareVersion,
batteryPercent: batteryPercent,
batteryHealthPercent: batteryHealthPercent,
isCharging: isCharging,
attributes: attributes
)
}
}Battery fields use a 0–100 scale. Use nil for unavailable readings and omit
unknown custom attributes. In workspaces with Devices enabled, firmware, hardware
revision, battery, and battery health appear in device details. Charging and
custom readings are available in the expanded snapshot event JSON.
The vendor hardware ID is the physical identity; the Core Bluetooth peripheral UUID is a scoped alias.
The SDK coalesces refreshes, skips unchanged snapshots, and reports active devices
again after customer or project changes. On disconnect, call
BTX.deviceSnapshots.disconnect(connectionKey:) to cancel pending work. This
stops snapshot reporting; it does not emit a disconnected event.
Diagnose connection failures
Create one attempt UUID for a connection attempt, including automatic retries. Report the final failure from your connection manager, with the stage, total elapsed time, retry count, and stable error code. BTX does not automatically observe Bluetooth callbacks.
import Foundation
import BTXClientKit
func recordConnectionFailure(
deviceID: String,
attemptID: UUID,
stage: String,
durationMs: Int,
retryCount: Int,
errorCode: String
) {
BTX.log(
"device_connection_failed",
level: .warning,
properties: [
"deviceId": deviceID,
"attemptID": attemptID.uuidString,
"transport": "bluetooth",
"connectionStage": stage,
"durationMs": durationMs,
"retryCount": retryCount,
"errorCode": errorCode
]
)
}Use the same deviceId as the health snapshot and keep attemptID consistent
through scanning, connecting, discovery, and notification setup. Measure duration
with a monotonic timer. Report device_connection_ready only after your app's
required services and subscriptions are available. Log transitions and outcomes,
not every radio callback.
Search Telemetry by device or attempt ID and expand events for their properties. These are host-defined events reviewed through search and JSON details, not an automatically assembled connection trace.
Diagnose firmware updates
Keep one update UUID through transfer, reboot, reconnect, verification, and
automatic retries. Record firmware_update_started after preflight checks pass.
Report a final failure only after your updater stops or exhausts its retry policy.
import Foundation
import BTXClientKit
func recordFirmwareUpdateFailure(
deviceID: String,
updateID: UUID,
targetVersion: String,
stage: String,
durationMs: Int,
retryCount: Int,
errorCode: String
) {
BTX.log(
"firmware_update_failed",
level: .error,
properties: [
"deviceId": deviceID,
"updateID": updateID.uuidString,
"targetVersion": targetVersion,
"updateStage": stage,
"durationMs": durationMs,
"retryCount": retryCount,
"errorCode": errorCode
]
)
}Use stages such as transfer, reboot, reconnect, and verification. Keep
failure codes bounded and use the same physical deviceId across logs and
snapshots. Your app owns update safety checks and recovery.
After reconnecting, read the version from the device. Emit
firmware_update_completed only when it matches the target; otherwise report
failed verification. A finished transfer alone is not a successful update.
Refresh the health snapshot with the actual installed version and emit only one
terminal outcome for each update. If the version cannot be read, report that
failure rather than inventing a value.
Search Telemetry by update ID to inspect the update's events. These events do not automatically create firmware rollout groups or success-rate charts. Refresh snapshots separately to update the device's displayed firmware version.