Use this file to discover all available pages before exploring further.
This guide explains how GBGBridge handles capabilities — the native features that a host application can provide to web journeys. It covers typed capability slots (recommended), custom capability registration, the legacy configuration approach, capability negotiation, permission state, and graceful degradation patterns.
Typed slots are the recommended way to declare capture capabilities. Setting a handler on a slot simultaneously declares support and provides the implementation.
With typed slots, there is no separate “declaration” step. A slot’s isSupported property is computed as handler != nil && isEnabled. When the web journey sends a capability.query request, only slots with handlers appear as supported.
// Not supported — no handler sethost.documentCapture.isSupported // false// Supported — handler is sethost.documentCapture.handler = { request in ... }host.documentCapture.isSupported // true// Temporarily disabled — handler set but isEnabled is falsehost.documentCapture.isEnabled = falsehost.documentCapture.isSupported // false
Typed slot handlers return CaptureResult values. The SDK encodes them into the bridge protocol format automatically — no manual JSONValue dictionary construction needed.
Each typed slot has a permissionState property. Populate it using CameraDetector so the web journey can check permissions before attempting capture:
let camera = CameraDetector.check()host.documentCapture.permissionState = camera.permissionStatehost.selfieCapture.permissionState = camera.permissionState
This information appears in the capability.query response:
For capabilities that don’t have a typed slot, for example, NFC and biometrics, use registerCustomCapability():
let host = BridgeHost(hostVersion: "1.0.0")host.registerCustomCapability("nfc.read", version: "1.0") { request, responder in guard NFCTagReaderSession.readingAvailable else { responder.respond( status: .unsupported, data: nil, error: BridgeErrorPayload( code: "NFC_NOT_AVAILABLE", message: "NFC reading is not available on this device", recoverable: false ) ) return } do { let chipData = try await readNFCChip() responder.respond( status: .success, data: [ "mrz": .string(chipData.mrz), "photo": .string(chipData.photo.base64EncodedString()) ], error: nil ) } catch { responder.respond( status: .error, data: nil, error: BridgeErrorPayload( code: "NFC_ERROR", message: error.localizedDescription, recoverable: true ) ) }}
Custom capabilities automatically appear in capability.query responses as supported. If a typed slot exists for the same ID, the typed slot takes precedence.
With this approach, you implement BridgeCapabilityHandler for each action:
public final class NFCReadHandler: BridgeCapabilityHandler, @unchecked Sendable { public let action = "nfc.read" public func handle(request: BridgeMessage, responder: BridgeResponder) async { // Handle the request and call responder.respond(...) }}
The web journey sends a capability.query request. GBGBridge’s built-in CapabilityQueryHandler responds automatically.When using init(hostVersion:), the response is built dynamically from typed slots and custom capabilities. When using init(configuration:), it uses the static BridgeConfiguration dictionary.
The permissionState field appears when the capability provides permission metadata (typed slots with permissionState set, or BridgeCapabilityInfo with permissionState non-nil).
The capability.query response includes an environment field ("ios", "android", or "web" for iframe hosts). The web journey uses both the environment and the capability flags to make routing decisions.
When a capability isn’t available, your integration has two main routes: fall back to a web-based equivalent, or check upfront and prevent the user from starting a journey that won’t complete. The patterns below show both.
The web journey checks capabilities and adapts its flow. If a web-based fallback exists for the capability, the journey uses it. If there is no web equivalent, the step is skipped entirely.
Journey: Document Capture -> NFC Read -> Face Match -> ResultIf camera.document is unsupported (web fallback exists):Journey: Document Capture (web) -> NFC Read -> Face Match -> ResultIf NFC is unsupported (no web fallback):Journey: Document Capture -> Face Match -> Result (NFC skipped)
The host app doesn’t need to do anything special — the web journey handles fallback and routing decisions based on the capability query response.
If the web journey sends a request for a capability the host doesn’t support, typed slots automatically respond with .unsupported when no handler is set. For custom capabilities, respond explicitly:
responder.respond( status: .unsupported, data: nil, error: BridgeErrorPayload( code: "CAPABILITY_UNAVAILABLE", message: "NFC is not available on this device", recoverable: false ))