Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.go.gbgplc.com/llms.txt

Use this file to discover all available pages before exploring further.

General

What iOS versions does GBGBridge support?

iOS 15.0 and later. This covers all iPhone and iPad models that support iOS 15.

Does GBGBridge have any external dependencies?

No. GBGBridge uses only Apple system frameworks: Foundation, WebKit, Combine, SwiftUI, and AVFoundation (for CameraDetector). It has zero third-party dependencies.

What Swift version is required?

Swift 5.9 or later. The framework is built with BUILD_LIBRARY_FOR_DISTRIBUTION enabled, which generates .swiftinterface files for binary module stability. This means you don’t need to match the exact Swift compiler version used to build the framework.

How is GBGBridge distributed?

As an XCFramework via Swift Package Manager (SPM). The package contains a pre-built binary target that includes slices for both iOS device (arm64) and iOS Simulator (arm64 + x86_64).

How large is the framework?

The GBGBridge binary is approximately 50 KB. It adds negligible size to your app.

Integration

Can I use GBGBridge with UIKit?

Yes. Use BridgeWebViewConfigurator to configure a WKWebView in your UIKit view controller. See the Embedding Guide for details.

Can I use multiple BridgeHost instances?

Each BridgeHost should be associated with one WKWebView. If you have multiple WebViews (e.g., multiple journey tabs), create a separate BridgeHost for each.

Can I change capabilities after initialization?

With typed slots (init(hostVersion:)), capabilities are inherently dynamic. Set or clear handler to change support, toggle isEnabled to temporarily disable, and update permissionState as permissions change. The capability query response is built dynamically on each query. With the configuration-based init, the capabilities dictionary is @Published and mutable, but the built-in CapabilityQueryHandler reads from a closure set at initialization. For fully dynamic capabilities in that path, replace the built-in handler with a custom one:
host.unregister(action: "capability.query")
host.register(handler: CapabilityQueryHandler(
    capabilities: { [weak host] in host?.capabilities ?? [:] },
    hostVersion: "1.0.0"
))

Do I need to handle the capability.query action myself?

No. BridgeHost automatically registers a CapabilityQueryHandler that responds to capability.query requests. With init(hostVersion:), it builds the response from typed slots and custom capabilities. With init(configuration:), it reads from the BridgeConfiguration.

What is the difference between typed slots and custom capabilities?

Typed slots (host.documentCapture, host.selfieCapture) are built-in CaptureCapability properties for well-known capture operations. They handle result encoding, busy rejection, and permission state automatically. Handlers return CaptureResult values. Custom capabilities (registerCustomCapability()) support any action. Handlers receive a BridgeResponder and build responses manually using JSONValue dictionaries. Use this for non-camera capabilities like NFC. Both appear in capability.query responses. If both are registered for the same ID, the typed slot takes precedence.

How does permission state work?

Each typed slot has a permissionState property (default: .notDetermined). Populate it using CameraDetector.check():
let camera = CameraDetector.check()
host.documentCapture.permissionState = camera.permissionState
This is included in the capability.query response as a permissionState field, allowing the web journey to detect permission issues before attempting capture.

What happens if the web journey sends a request for an action I haven’t registered?

The request is added to host.pendingRequests and bridgeHost(_:unhandledRequest:) is called on the delegate. You can respond to it manually via host.respond(to:status:data:error:). If you don’t respond, the request sits in pendingRequests indefinitely. The web journey may implement its own timeout.

Messaging

Are messages guaranteed to be delivered?

Messages sent via the bridge are in-process or inter-process calls through WebKit. They are delivered reliably as long as:
  • The WebView is attached to the host.
  • The web page has not navigated away.
  • window.GBGBridge.receive is defined on the web side.
There is no built-in retry or delivery confirmation mechanism.

What is the maximum message size?

There is no hard limit imposed by GBGBridge. However, very large messages (e.g., multi-megabyte Base64 images) can cause memory pressure. For large data transfers, consider passing file paths instead of inline data.

Can I send messages before the web page loads?

Messages sent before the page loads (or before the web journey sets up its receive() function) will be lost. The default bootstrap script creates a no-op receive(), so the call won’t error — but the data won’t be processed.
Wait for a signal from the web journey (e.g., a journey.ready event) before sending messages.

Is the message order preserved?

Messages are processed in the order they arrive. Responses are sent in the order respond() is called. WebKit’s evaluateJavaScript calls are executed sequentially.

What happens to events — does anyone respond to them?

Events are fire-and-forget. They have no expected response. Both the native host and the web journey can send events. Use events for notifications, state updates, and lifecycle signals.

Capabilities

How does the web journey know what capabilities are available?

The web journey sends a capability.query request. The built-in handler responds with the environment ("ios"), host version, and a map of all declared capabilities with their supported status, version, and optionally permissionState.

What if a capability is hardware-dependent?

For camera capabilities, use CameraDetector.check() and conditionally set the handler:
let camera = CameraDetector.check()
if camera.hardwareAvailable {
    host.documentCapture.handler = { ... }
    host.documentCapture.permissionState = camera.permissionState
}
For NFC, check at initialization time:
import CoreNFC

if NFCTagReaderSession.readingAvailable {
    host.registerCustomCapability("nfc.read", version: "1.0") { request, responder in
        // Handle NFC
    }
}

What NFC entitlements do I need?

To use NFC in your host app:
  1. Add the Near Field Communication Tag Reading capability in Xcode.
  2. Include com.apple.developer.nfc.readersession.formats in your entitlements.
  3. Add NSNFCReaderUsageDescription to Info.plist.
GBGBridge itself does not require these — they are only needed if your NFC handler calls CoreNFC APIs.

Security

Is communication between the host and web journey encrypted?

Communication uses WebKit’s internal IPC mechanism. It does not traverse the network, so TLS is not applicable. Messages are memory-to-memory within the device.

Can other apps intercept bridge messages?

No. The WebView runs within your app’s sandbox. The script message handler channel is private to your app process.

Should I validate incoming message data?

Yes. Treat data from the web journey the same way you would treat user input. Validate required fields, check value ranges, and reject malformed requests.

Debugging

How do I see what messages are being exchanged?

Use the BridgeHostDelegate to log all messages:
func bridgeHost(_ host: BridgeHost, didReceive message: BridgeMessage) {
    print("[\(message.type.rawValue)] \(message.payload.action)")
}
Or observe host.$receivedMessages via Combine.

Can I use Safari Web Inspector?

Yes. Enable Web Inspector on your device (Settings → Safari → Advanced → Web Inspector), connect to a Mac, and inspect the WebView from Safari’s Develop menu. You can examine window.GBGBridge and test messages from the console.

Why do I see “WebView not attached” errors?

The BridgeHost holds a weak reference to the WebView. If the WebView is deallocated, sending messages will produce this error. Ensure the WebView’s lifetime matches or exceeds the host’s lifetime.

Next Steps