> ## 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.

# Troubleshooting

> Diagnose and resolve common GBGBridge integration issues.

This guide helps diagnose and resolve common issues when integrating GBGBridge.

## Diagnostic Tools

`BridgeHost` exposes several built-in diagnostic surfaces — error state, a message log, and a list of pending requests — that should be your first stops when something looks off. Combined with Safari Web Inspector for the web side, these cover most debugging scenarios.

### Observing lastError

The first thing to check is `BridgeHost.lastError`. It captures the most recent error from message decoding, JavaScript evaluation, or WebView attachment.

```swift theme={null}
// In SwiftUI
Text(host.lastError ?? "No errors")

// Via Combine
host.$lastError
    .compactMap { $0 }
    .sink { error in
        print("[Bridge Error] \(error)")
    }
    .store(in: &cancellables)
```

### Message Log

`BridgeHost.receivedMessages` contains every successfully decoded message. Use it to verify message flow.

```swift theme={null}
// Print all received messages
for message in host.receivedMessages {
    print("[\(message.type.rawValue)] \(message.payload.action) — \(message.correlationId)")
}
```

### Pending Request Inspection

`BridgeHost.pendingRequests` shows requests awaiting a response. If this list grows unexpectedly, it means requests are arriving for actions with no registered handler.

```swift theme={null}
if !host.pendingRequests.isEmpty {
    print("Unhandled requests:")
    for req in host.pendingRequests {
        print("  - \(req.payload.action) (\(req.correlationId))")
    }
}
```

### Safari Web Inspector

For inspecting the web side of the bridge:

1. Enable **Web Inspector** on your device: Settings → Safari → Advanced → Web Inspector.
2. Connect your device to a Mac.
3. In Safari on Mac, go to **Develop → \[Your Device] → \[Your App]**.
4. Use the console to inspect `window.GBGBridge` and test message sending.

```javascript theme={null}
// In Safari Web Inspector console:
console.log(window.GBGBridge);  // Should show the bridge namespace
console.log(typeof window.GBGBridge.receive);  // Should be "function"
```

## Common Issues

Most bridge problems fall into a small set of recurring categories — message routing, capability discovery, network/ATS, and SwiftUI state plumbing. Each entry below describes the symptoms, the likely causes, and the fix.

### Messages Not Being Received by the Host

**Symptoms:** The web journey sends messages, but `receivedMessages` stays empty.

#### Possible causes

1. **Wrong message handler name.** The web journey must use exactly:
   ```javascript theme={null}
   window.webkit.messageHandlers.gbgBridge.postMessage(message);
   ```
   Note the lowercase `g` in `gbgBridge`.

2. **WebView not configured.** Ensure you used `BridgeWebView` or `BridgeWebViewConfigurator.configure(_:host:)`. A plain `WKWebView` does not have the message handler registered.

3. **Bootstrap script not injected.** If the web journey tries to send a message before the bootstrap script runs, `window.webkit.messageHandlers.gbgBridge` may not exist. The bootstrap script runs at document start, but if you're configuring the WebView after navigation begins, messages may be lost.

**Fix:** Always configure the WebView before loading any content.

***

### Messages Not Being Received by the Web Journey

**Symptoms:** `send(event:data:)` or `respond(to:...)` is called, but the web journey doesn't receive the message.

#### Possible causes

1. **WebView not attached.** Check `lastError` — it will say `"WebView not attached"` if the WebView reference was lost.

2. **`window.GBGBridge.receive` not defined.** The web journey must replace the no-op `receive()` with its own implementation. Check from Safari Web Inspector:
   ```javascript theme={null}
   console.log(window.GBGBridge.receive.toString());
   ```

3. **JavaScript evaluation error.** Check `lastError` for messages starting with `"evaluateJavaScript failed:"`.

4. **Page navigated away.** If the WebView navigated to a different page, the bridge context is lost. The bootstrap script re-runs on each page load, but the web journey must reinitialize its `receive()` function.

**Fix:** Ensure the web journey sets up `window.GBGBridge.receive` after page load and that the WebView is attached to the host.

***

### Message Decoding Failures

**Symptoms:** `lastError` says `"Failed to decode message: ..."`.

#### Possible causes

1. **Malformed JSON.** The web journey is sending a message that doesn't match the `BridgeMessage` structure.

2. **Missing required fields.** Every message must have: `version`, `correlationId`, `type`, `timestamp`, and `payload` (with at least `action`).

3. **Wrong type for `type` field.** Must be one of: `"request"`, `"response"`, `"event"`.

**Fix:** Validate the message structure on the web side before sending. A valid message looks like:

```javascript theme={null}
{
  "version": "1.0",
  "correlationId": "unique-id",
  "type": "request",
  "timestamp": Date.now(),
  "payload": {
    "action": "camera.capture",
    "data": {}
  }
}
```

***

### Capability Query Returns Unexpected Results

**Symptoms:** The web journey receives a `capability.query` response, but capabilities are missing or incorrect.

#### Possible causes

1. **Typed slot without a handler.** When using `init(hostVersion:)`, a slot only appears as supported if `handler` is set *and* `isEnabled` is `true`. If you forgot to set a handler, the slot won't appear.

2. **Slot is disabled.** Check that `isEnabled` hasn't been set to `false` on the slot.

3. **Capabilities not declared (configuration-based init).** When using `init(configuration:)`, only capabilities in the `BridgeConfiguration` dictionary are reported.

4. **Custom capability not registered.** If using `registerCustomCapability()`, ensure it was called before the query.

**Fix:** For typed slots, verify `handler` is set: `host.documentCapture.isSupported` should be `true`. For configuration-based init, review your `BridgeConfiguration`.

***

### Permission State Not Appearing in Query Response

**Symptoms:** The `permissionState` field is missing from the capability query response.

#### Possible causes

1. **Not using typed slots.** Permission state is only included automatically when using `init(hostVersion:)` with typed slots that have `permissionState` set.

2. **CameraDetector not called.** The default `permissionState` is `.notDetermined`. Call `CameraDetector.check()` and assign the result.

3. **Using configuration-based init without permissionState.** The `BridgeCapabilityInfo` `permissionState` field defaults to `nil`. Set it explicitly.

**Fix:**

```swift theme={null}
let camera = CameraDetector.check()
host.documentCapture.permissionState = camera.permissionState
```

***

### Handler Not Called for Requests

**Symptoms:** A request arrives (visible in `receivedMessages`) but the handler's `handle()` method is never called. The request appears in `pendingRequests`.

#### Possible causes

1. **Typed slot handler not set.** When using `init(hostVersion:)`, ensure `host.documentCapture.handler` (or `selfieCapture.handler`) is set. Without a handler, requests for `camera.document.capture` go to `pendingRequests`.

2. **Action mismatch.** For custom capabilities or `BridgeCapabilityHandler`, the `action` property must exactly match the request's `payload.action`. Check for typos and case sensitivity.

3. **Handler not registered.** For the `register(handler:)` path, ensure it was called before the request arrived.

4. **Slot is disabled.** Check that `isEnabled` is `true` on the typed slot.

**Fix:** For typed slots, verify the handler is set and the slot is enabled. For protocol-based handlers, verify the action string matches:

```swift theme={null}
// Typed slot — action is "camera.document.capture"
host.documentCapture.handler = { ... }

// Protocol-based handler — action must match exactly
let action = "camera.document.capture"  // Must match web journey's payload.action
```

***

### App Transport Security Errors

**Symptoms:** The WebView shows a blank page or an error. The console shows ATS-related errors.

**Fix for local development:**

```xml theme={null}
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsLocalNetworking</key>
    <true/>
</dict>
```

**Fix for production:** Use HTTPS for all journey URLs. Do not add `NSAllowsArbitraryLoads`.

***

### WebView Shows Blank Page

**Symptoms:** The WebView renders but shows nothing.

#### Possible causes

1. **Invalid URL.** Check that the URL is reachable.
2. **ATS blocking.** See above.
3. **CORS issues.** If the journey loads resources from other domains, check CORS headers.
4. **JavaScript error.** Connect Safari Web Inspector to check for console errors.

**Fix:** Test the URL in Safari first to rule out web-side issues.

***

### Memory Warnings or Crashes

**Symptoms:** App receives memory warnings or crashes when using the bridge.

#### Possible causes

1. **Large payloads.** Sending very large Base64-encoded images through the bridge can cause memory pressure. Consider passing file paths instead of raw data.
2. **Message accumulation.** `receivedMessages` grows without bound. If you're in a long-running session, this array can become large.

**Fix for large payloads:**

```swift theme={null}
// Instead of sending raw image data:
// data: ["imageBase64": .string(largeBase64String)]

// Send a file path:
responder.respond(
    status: .success,
    data: ["imagePath": .string(tempFilePath)],
    error: nil
)
```

**Fix for message accumulation:** Periodically clear old messages if your session is long-running.

***

### SwiftUI View Not Updating

**Symptoms:** `BridgeHost` state changes (e.g., `lastError`, `receivedMessages`) don't trigger view updates.

#### Possible causes

1. **Using `@ObservedObject` where `@StateObject` is needed.** If the view owns the host, use `@StateObject` to prevent re-creation on re-renders.
2. **Host created outside the view hierarchy.** Ensure the host is referenced via `@StateObject` or `@ObservedObject`.

**Fix:**

```swift theme={null}
// Owner view — creates the host
struct ParentView: View {
    @StateObject private var host = BridgeHost(...)  // Use @StateObject
    var body: some View {
        ChildView(host: host)
    }
}

// Child view — receives the host
struct ChildView: View {
    @ObservedObject var host: BridgeHost  // Use @ObservedObject
    var body: some View { ... }
}
```

### Typed Slot Returns "BUSY" Error

**Symptoms:** The web journey receives a `BUSY` error when sending a capture request.

**Cause:** A previous capture request is still in progress on the same slot (`activeRequest` is non-nil).

**Fix:** Ensure the previous request completes before sending another. Call `host.documentCapture.complete(...)` when the camera UI finishes or is dismissed. Check the `onDismiss` closure of your `fullScreenCover`:

```swift theme={null}
.fullScreenCover(isPresented: $showCamera, onDismiss: {
    if host.documentCapture.activeRequest != nil {
        host.documentCapture.complete(.cancelled(reason: "Dismissed"))
    }
}) { ... }
```

## Debugging Checklist

When something isn't working, run through these checks in whichever order makes most sense for the symptom — they're independent, not sequential:

* [ ] Check `host.lastError` for error messages.
* [ ] Check `host.receivedMessages` to see if messages are arriving.
* [ ] Check `host.pendingRequests` for unhandled requests.
* [ ] For typed slots: verify `handler` is set and `isSupported` is `true`.
* [ ] For typed slots: check `activeRequest` isn't stuck — i.e. a previous request didn't complete.
* [ ] Connect Safari Web Inspector to examine the web side.
* [ ] Verify handler `action` strings match request actions exactly.
* [ ] Ensure the WebView was configured via `BridgeWebView` or `BridgeWebViewConfigurator`.
* [ ] Confirm the journey URL is reachable and uses HTTPS.
* [ ] Check Info.plist for required permission descriptions.
* [ ] Verify `@StateObject` vs `@ObservedObject` usage.

## Next Steps

* [FAQ](/docs/go-v2/developer-integration/sdks/ios/faq) — Common questions
* [Messaging Guide](/docs/go-v2/developer-integration/sdks/ios/messaging) — Message flow details
* [Security Guide](/docs/go-v2/developer-integration/sdks/ios/security) — Security best practices
