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.

GBGBridge ships two stub camera views — StubDocumentCameraView and StubSelfieCameraView — that let you prove the bridge works end-to-end without installing the SmartCapture SDKs.
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.

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

FieldValue
imageDataPNG-encoded photo from the rear camera (device) or placeholder image (Simulator)
width / heightPixel 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.
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

FieldValue
previewImageDataJPEG-encoded selfie from the front camera (device) or placeholder image (Simulator)
width / heightPixel dimensions of the captured image
mimeType"image/jpeg"
encryptedBlobRaw JPEG data (placeholder)
unencryptedBlobRaw 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
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.

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.
For a complete step-by-step walkthrough of the swap, see Tutorial Part 2: Integrate Smart Capture SDKs. It covers obtaining the frameworks, setting up the compiler flag, and building the wrapper views.

Before (stub)

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

After (real SDK)

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