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

# Embedding

> SwiftUI and UIKit patterns for displaying journey WebViews.

This guide covers how to embed the bridge-connected WebView in your iOS application, including SwiftUI and UIKit integration patterns.

## SwiftUI Integration

For SwiftUI apps, `BridgeWebView` is the fastest path: drop it into a view, give it a URL and a host, and you have a fully wired bridge. Use the patterns below for state observation, child-view passing, and dynamic URLs.

### Using BridgeWebView

`BridgeWebView` is the recommended way to embed a bridge-connected WebView in SwiftUI. It handles all setup automatically.

```swift theme={null}
import GBGBridge
import SwiftUI

struct JourneyScreen: View {
    @StateObject private var host = BridgeHost(hostVersion: "1.0.0")

    var body: some View {
        VStack {
            BridgeWebView(
                url: URL(string: "https://journey.example.com")!,
                host: host
            )
        }
        .onAppear {
            host.documentCapture.handler = { [weak host] request in
                guard let host else { return .cancelled(reason: "Host deallocated") }
                return await host.documentCapture.awaitCompletion()
            }
        }
    }
}
```

### Observing Bridge State

Because `BridgeHost` is an `ObservableObject`, you can react to state changes directly in your SwiftUI views.

```swift theme={null}
struct JourneyScreen: View {
    @StateObject private var host = BridgeHost(hostVersion: "1.0.0")

    var body: some View {
        ZStack {
            BridgeWebView(
                url: URL(string: "https://journey.example.com")!,
                host: host
            )

            // Show error banner when an error occurs
            if let error = host.lastError {
                VStack {
                    Text(error)
                        .foregroundColor(.white)
                        .padding()
                        .background(Color.red.cornerRadius(8))
                        .padding()
                    Spacer()
                }
                .transition(.move(edge: .top))
            }
        }
    }
}
```

### Passing the Host to Child Views

Use `@ObservedObject` in child views that receive the host as a parameter.

```swift expandable theme={null}
struct JourneyContainer: View {
    @StateObject private var host = BridgeHost(hostVersion: "1.0.0")

    var body: some View {
        NavigationStack {
            JourneyWebView(host: host)
                .toolbar {
                    ToolbarItem(placement: .navigationBarTrailing) {
                        MessageCountBadge(host: host)
                    }
                }
        }
    }
}

struct JourneyWebView: View {
    @ObservedObject var host: BridgeHost

    var body: some View {
        BridgeWebView(
            url: URL(string: "https://journey.example.com")!,
            host: host
        )
    }
}

struct MessageCountBadge: View {
    @ObservedObject var host: BridgeHost

    var body: some View {
        Text("\(host.receivedMessages.count)")
            .font(.caption)
            .padding(6)
            .background(Color.blue)
            .clipShape(Circle())
            .foregroundColor(.white)
    }
}
```

### Dynamic URL Changes

`BridgeWebView` supports URL changes. If you update the URL binding, the WebView navigates to the new URL.

```swift theme={null}
struct DynamicJourney: View {
    @StateObject private var host = BridgeHost(hostVersion: "1.0.0")
    @State private var journeyURL = URL(string: "https://journey.example.com/step1")!

    var body: some View {
        VStack {
            BridgeWebView(url: journeyURL, host: host)

            Button("Next Step") {
                journeyURL = URL(string: "https://journey.example.com/step2")!
            }
        }
    }
}
```

## UIKit Integration

For UIKit apps (or SwiftUI apps that need a custom `WKWebViewConfiguration`), use `BridgeWebViewConfigurator` to wire the bridge into a WebView you create yourself.

### Using BridgeWebViewConfigurator

For UIKit-based apps, use `BridgeWebViewConfigurator` to configure a `WKWebView` with the bridge.

```swift expandable theme={null}
import GBGBridge
import UIKit
import WebKit

class JourneyViewController: UIViewController {
    private let host: BridgeHost
    private var webView: WKWebView!

    init() {
        self.host = BridgeHost(hostVersion: "1.0.0")
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        // Option A: Create a new configured WebView
        webView = BridgeWebViewConfigurator.makeWebView(host: host)

        // Option B: Configure an existing WebView
        // let config = WKWebViewConfiguration()
        // webView = WKWebView(frame: .zero, configuration: config)
        // BridgeWebViewConfigurator.configure(webView, host: host)

        webView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(webView)

        NSLayoutConstraint.activate([
            webView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            webView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            webView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])

        let url = URL(string: "https://journey.example.com")!
        webView.load(URLRequest(url: url))
    }
}
```

### Configuring an Existing WebView

If you have a `WKWebView` created with a custom `WKWebViewConfiguration`, use `BridgeWebViewConfigurator.configure(_:host:)`.

```swift theme={null}
let config = WKWebViewConfiguration()
config.allowsInlineMediaPlayback = true

let webView = WKWebView(frame: .zero, configuration: config)
BridgeWebViewConfigurator.configure(webView, host: host)
```

This approach is useful when you need custom WebView settings (media playback, data detection, process pools, etc.) that `makeWebView` doesn't configure.

## Lifecycle Considerations

The `BridgeHost` and the WebView have different ownership semantics — the host holds a weak reference to the WebView, and handlers should be set up before content loads. Keep these in mind to avoid leaks, attachment errors, and missed messages.

### Host Lifetime

The `BridgeHost` should live at least as long as the WebView. In SwiftUI, use `@StateObject` to ensure the host isn't recreated on view updates. In UIKit, hold a strong reference as a property.

### WebView Detachment

`BridgeHost` holds a **weak** reference to the WebView. If the WebView is deallocated while the host is still alive, attempts to send messages will set `lastError` to `"WebView not attached"`. This is by design — it prevents retain cycles.

### Re-attaching a WebView

If you need to replace the WebView (e.g., after a navigation reset), call `BridgeWebViewConfigurator.configure(_:host:)` with the new WebView. The host automatically updates its internal reference.

### Setting Up Handlers

Set handlers on typed slots or register custom capabilities before the WebView loads content. If the web journey sends a request before a handler is available, the request goes to `pendingRequests`.

```swift theme={null}
// Set up typed slots
host.documentCapture.handler = { [weak host] request in
    guard let host else { return .cancelled(reason: "Host deallocated") }
    return await host.documentCapture.awaitCompletion()
}

// Register custom capabilities
host.registerCustomCapability("nfc.read") { request, responder in
    // Handle NFC
}

// Then load the journey
webView.load(URLRequest(url: journeyURL))
```

## Navigation Delegate

`BridgeWebView` provides a basic `WKNavigationDelegate` via its `Coordinator`. If you need custom navigation behavior (e.g., intercepting links, handling authentication challenges), you have two options:

1. **UIKit path:** Use `BridgeWebViewConfigurator` and set your own navigation delegate on the WebView.
2. **SwiftUI path:** Build a custom `UIViewRepresentable` that uses `BridgeWebViewConfigurator` internally.

```swift expandable theme={null}
struct CustomBridgeWebView: UIViewRepresentable {
    let url: URL
    @ObservedObject var host: BridgeHost

    func makeUIView(context: Context) -> WKWebView {
        let webView = BridgeWebViewConfigurator.makeWebView(host: host)
        webView.navigationDelegate = context.coordinator
        webView.load(URLRequest(url: url))
        return webView
    }

    func updateUIView(_ webView: WKWebView, context: Context) {}

    func makeCoordinator() -> Coordinator {
        Coordinator()
    }

    class Coordinator: NSObject, WKNavigationDelegate {
        func webView(
            _ webView: WKWebView,
            decidePolicyFor navigationAction: WKNavigationAction
        ) async -> WKNavigationActionPolicy {
            // Custom navigation logic
            if navigationAction.navigationType == .linkActivated,
               let url = navigationAction.request.url,
               url.host != "journey.example.com" {
                await UIApplication.shared.open(url)
                return .cancel
            }
            return .allow
        }
    }
}
```

## Next Steps

* [Messaging Guide](/docs/go-v2/developer-integration/sdks/ios/messaging) — Learn to send events and handle requests
* [Capability Handling Guide](/docs/go-v2/developer-integration/sdks/ios/capability-handling) — Register handlers for native features
* [API Reference](/docs/go-v2/developer-integration/sdks/ios/api-reference) — Detailed reference for `BridgeWebView` and `BridgeWebViewConfigurator`
