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

# Stub Camera Views

> Test your integration end-to-end without SmartCapture SDKs.

GBGBridge ships two stub camera views — `StubDocumentCameraView` and `StubSelfieCameraView` — that let you prove the bridge works end-to-end without installing the SmartCapture SDKs.

<Warning>
  Development and testing only. On a real device the stub views use `UIImagePickerController` to take a plain photo. On the Simulator they display programmatically drawn placeholder images with a shutter button. They do not perform document detection, auto-cropping, liveness checks, or biometric encryption. Do not ship them in production.
</Warning>

## When to Use Stubs

* **Early integration** — You want to verify the bridge protocol, capability negotiation, and data flow before the SmartCapture SDKs are available or configured.
* **CI / automated testing** — Stubs work on Simulator (displaying placeholder images) without requiring camera hardware.
* **Prototyping** — You're building the host app shell and want a capture flow that "just works" while you focus on other parts of the integration.

## StubDocumentCameraView

Presents the rear camera on a real device, or a programmatically drawn placeholder document image on Simulator. Returns a `DocumentCaptureResult` containing the PNG-encoded image.

```swift theme={null}
import GBGBridge

.fullScreenCover(isPresented: $showDocumentCamera, onDismiss: {
    if host.documentCapture.activeRequest != nil {
        host.documentCapture.complete(.cancelled(reason: "Dismissed"))
    }
}) {
    StubDocumentCameraView(
        onCaptured: { result in
            host.documentCapture.complete(.document(result))
        },
        onCancelled: {
            host.documentCapture.complete(.cancelled(reason: "User dismissed"))
        }
    )
}
```

### What you get

| Field              | Value                                                                            |
| ------------------ | -------------------------------------------------------------------------------- |
| `imageData`        | PNG-encoded photo from the rear camera (device) or placeholder image (Simulator) |
| `width` / `height` | Pixel dimensions of the captured image                                           |
| `mimeType`         | `"image/png"`                                                                    |

### What you don't get

* Document edge detection and auto-crop
* Blur / glare / quality scoring
* Guided capture overlay with real-time feedback
* Auto-capture on frame quality threshold

## StubSelfieCameraView

Presents the front camera on a real device, or a programmatically drawn face silhouette on Simulator. Returns a `SelfieCaptureResult` with the JPEG image and placeholder biometric blobs.

```swift theme={null}
import GBGBridge

.fullScreenCover(isPresented: $showSelfieCamera, onDismiss: {
    if host.selfieCapture.activeRequest != nil {
        host.selfieCapture.complete(.cancelled(reason: "Dismissed"))
    }
}) {
    StubSelfieCameraView(
        onCaptured: { result in
            host.selfieCapture.complete(.selfie(result))
        },
        onCancelled: {
            host.selfieCapture.complete(.cancelled(reason: "User dismissed"))
        }
    )
}
```

### What you get

| Field              | Value                                                                               |
| ------------------ | ----------------------------------------------------------------------------------- |
| `previewImageData` | JPEG-encoded selfie from the front camera (device) or placeholder image (Simulator) |
| `width` / `height` | Pixel dimensions of the captured image                                              |
| `mimeType`         | `"image/jpeg"`                                                                      |
| `encryptedBlob`    | Raw JPEG data (placeholder)                                                         |
| `unencryptedBlob`  | Raw JPEG data (placeholder)                                                         |

### What you don't get

* Liveness detection (active or passive)
* Face mesh / landmark analysis
* Encrypted biometric blobs that pass server-side validation
* Guided selfie overlay with positioning feedback

<Note>
  The `encryptedBlob` and `unencryptedBlob` fields contain the raw JPEG image data as a stand-in. They are structurally valid (non-empty `Data`) so the bridge protocol works, but they will not pass liveness verification on the server. This is by design — the stubs exist to prove the integration, not to produce real biometric data.
</Note>

## Simulator Support

On Simulator, there is no camera hardware. Both stub views automatically display programmatically drawn placeholder images — a passport-style document card for `StubDocumentCameraView` and a face silhouette for `StubSelfieCameraView` — with a shutter-style capture button. Tapping the shutter generates a synthetic image and returns it through the normal callback, so you can exercise the full capture flow in CI or on your Mac without a physical device.

## Swapping Stubs for Real SDKs

The stub views are a drop-in replacement for the real SmartCapture views. The handler setup and bridge wiring stay the same — only the view inside `fullScreenCover` changes.

<Tip>For a complete step-by-step walkthrough of the swap, see [Tutorial Part 2: Integrate Smart Capture SDKs](/docs/go-v2/developer-integration/sdks/ios/tutorial-smart-capture). It covers obtaining the frameworks, setting up the compiler flag, and building the wrapper views.</Tip>

### Before (stub)

```swift theme={null}
.fullScreenCover(isPresented: $showDocumentCamera) {
    StubDocumentCameraView(
        onCaptured: { result in
            host.documentCapture.complete(.document(result))
        },
        onCancelled: {
            host.documentCapture.complete(.cancelled(reason: "User dismissed"))
        }
    )
}
```

### After (real SDK)

```swift theme={null}
.fullScreenCover(isPresented: $showDocumentCamera) {
    SmartCaptureDocumentView(
        documentSide: .front,
        onCaptured: { imageData, width, height in
            host.documentCapture.complete(.document(
                DocumentCaptureResult(imageData: imageData, width: width, height: height)
            ))
        },
        onFailed: { message in
            host.documentCapture.complete(.failed(
                code: "CAPTURE_FAILED", message: message, recoverable: true
            ))
        }
    )
}
```

The key differences when swapping:

1. **View type** — Replace `StubDocumentCameraView` / `StubSelfieCameraView` with your SDK-backed view.
2. **Callback shape** — The real SDK views may have different callback signatures (e.g., separate `onCaptured` and `onFailed` callbacks instead of a single `DocumentCaptureResult`). Construct the `DocumentCaptureResult` or `SelfieCaptureResult` in the callback.
3. **Nothing else changes** — The handler on the typed slot, the `awaitCompletion()` / `complete()` pattern, the bridge protocol, and the `fullScreenCover` presentation logic all stay the same.

## Complete Example

A minimal SwiftUI view using both stub views:

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

struct StubJourneyView: View {
    @StateObject private var host = BridgeHost(hostVersion: "1.0.0")
    @State private var showDocumentCamera = false
    @State private var showSelfieCamera = false
    let journeyURL: URL

    var body: some View {
        BridgeWebView(url: journeyURL, host: host)
            .onAppear { setupHandlers() }
            .onChange(of: host.documentCapture.activeRequest?.correlationId) { _ in
                showDocumentCamera = host.documentCapture.activeRequest != nil
            }
            .onChange(of: host.selfieCapture.activeRequest?.correlationId) { _ in
                showSelfieCamera = host.selfieCapture.activeRequest != nil
            }
            .fullScreenCover(isPresented: $showDocumentCamera, onDismiss: {
                if host.documentCapture.activeRequest != nil {
                    host.documentCapture.complete(.cancelled(reason: "Dismissed"))
                }
            }) {
                StubDocumentCameraView(
                    onCaptured: { result in
                        host.documentCapture.complete(.document(result))
                    },
                    onCancelled: {
                        host.documentCapture.complete(.cancelled(reason: "User dismissed"))
                    }
                )
            }
            .fullScreenCover(isPresented: $showSelfieCamera, onDismiss: {
                if host.selfieCapture.activeRequest != nil {
                    host.selfieCapture.complete(.cancelled(reason: "Dismissed"))
                }
            }) {
                StubSelfieCameraView(
                    onCaptured: { result in
                        host.selfieCapture.complete(.selfie(result))
                    },
                    onCancelled: {
                        host.selfieCapture.complete(.cancelled(reason: "User dismissed"))
                    }
                )
            }
    }

    private func setupHandlers() {
        let camera = CameraDetector.check()
        host.documentCapture.permissionState = camera.permissionState
        host.selfieCapture.permissionState = camera.permissionState

        host.documentCapture.handler = { [weak host] request in
            guard let host else { return .cancelled(reason: "Host deallocated") }
            return await host.documentCapture.awaitCompletion()
        }

        host.selfieCapture.handler = { [weak host] request in
            guard let host else { return .cancelled(reason: "Host deallocated") }
            return await host.selfieCapture.awaitCompletion()
        }
    }
}
```

## Next Steps

* [Tutorial Part 2: Integrate Smart Capture SDKs](/docs/go-v2/developer-integration/sdks/ios/tutorial-smart-capture) — Step-by-step guide to replacing stubs with production SDKs
* [Integration Checklist](/docs/go-v2/developer-integration/sdks/ios/integration-checklist) — End-to-end setup walkthrough
* [Capability Handling Guide](/docs/go-v2/developer-integration/sdks/ios/capability-handling) — How typed slots and custom capabilities work
* [Embedding Guide](/docs/go-v2/developer-integration/sdks/ios/embedding) — SwiftUI and UIKit integration patterns
