Replace stub camera views with production Smart Capture SDKs for document scanning and face capture with liveness detection.
This tutorial picks up where Part 1 left off. You already have a working iOS app that runs a GBG Go identity journey with stub camera views. Now you will replace those stubs with the real Smart Capture SDKs — adding guided document scanning, face capture with liveness detection, and encrypted biometric blobs.The change is small. The bridge architecture you built in Part 1 was designed so that swapping camera views is a minimal edit. The handler setup, awaitCompletion()/complete() pattern, and all bridge wiring stay the same. Only the views inside fullScreenCover change.
Reference app: The complete source code is on the part-2-smart-capture branch of the reference repository. To see exactly what changed from Part 1, diff the branches:
Contact your GBG account representative to obtain these frameworks:
Framework
Purpose
Document.xcframework
Document scanning with guided capture, auto-crop, and quality scoring
FaceCamera.xcframework
Face capture with liveness detection and encrypted biometric blobs
IDLiveFaceCamera.xcframework
Runtime dependency of FaceCamera
IDLiveFaceIAD.xcframework
Runtime dependency of FaceCamera
FaceCamera.xcframework links against IDLiveFaceCamera and IDLiveFaceIAD at runtime. If you add FaceCamera but forget the other two, the app crashes at launch with a “Library not loaded” error.
The Smart Capture SDKs ship as four .xcframework bundles. Drop them into the project, embed and sign them, then enable the compiler flag — the rest of the section walks through each step.
Open GBGGoReference/GBGGoReference.xcodeproj in Xcode.
Select the GBGGoReference target.
Go to General > Frameworks, Libraries, and Embedded Content.
Click +, then Add Other > Add Files.
Select all four .xcframework bundles from the Frameworks/ directory.
Set each to Embed & Sign.
The project already includes $(PROJECT_DIR)/Frameworks in its framework search paths. You only need to add the frameworks to the target’s embedded content.
The Smart Capture integration is gated behind a compile-time flag called SMART_CAPTURE_ENABLED. This flag is off by default, so the app builds and runs with stubs even if you haven’t added the frameworks yet.
In Xcode, select the GBGGoReference target.
Go to Build Settings and make sure “All” is selected, not “Basic”.
Search for Active Compilation Conditions (SWIFT_ACTIVE_COMPILATION_CONDITIONS).
Add SMART_CAPTURE_ENABLED to both the Debug and Release configurations.
With the flag set, the app compiles the Smart Capture wrapper views and uses them on a physical device. On Simulator, it still falls back to stubs regardless of the flag.
The entire file is wrapped in #if SMART_CAPTURE_ENABLED. When the flag is off, this file is invisible to the compiler — no import Document, no dependency on the framework.
Selfie capture follows the same pattern as document capture: a SwiftUI wrapper around the SDK’s view that builds a SelfieCaptureResult from the raw output and forwards it to the bridge slot. The wrapper handles SDK initialisation, runs the liveness flow, and surfaces failures as recoverable bridge errors.Create SmartCaptureFaceView.swift in the Sources/Capture/ group:
The stub views return the raw JPEG data in both encryptedBlob and unencryptedBlob as a placeholder. This is structurally valid so the bridge protocol works, but it will not pass server-side liveness verification. The real FaceCamera SDK produces properly encrypted biometric data.
Conditional compilation (#if) has three advantages over a runtime toggle:
Zero overhead. When the flag is off, the Smart Capture code does not exist in the binary. There are no unused framework imports and no dead code.
No accidental dependency. Without the flag, the project compiles without the Smart Capture frameworks. A runtime check would still require the frameworks to be linked.
Clear separation. The #if/#else blocks make it obvious which code path runs in each configuration. Reviewers see the stub and real implementations side by side.
Part 2 also adds permission state detection in configureHandlers():
private func configureHandlers() { let camera = CameraDetector.check() host.documentCapture.permissionState = camera.permissionState host.selfieCapture.permissionState = camera.permissionState // ... handler assignment (unchanged from Part 1)}
CameraDetector.check() queries the device’s camera hardware availability and permission state. Setting permissionState on the typed slots means the bridge’s built-in capability.query handler can report accurate permission information to the web journey — allowing the journey to adapt its flow if camera access is denied or restricted.
Connect a physical iOS device and select it in Xcode.
Press Cmd+R to build and run.
On the Setup screen, enter http://<your-mac-ip>:3000 as the server URL.
Tap Start Journey.
When the journey requests a document capture, the SmartCapture document scanner appears — with a guided overlay, real-time edge detection, and auto-capture.
When the journey requests a selfie, the FaceCamera SDK appears — with face positioning guidance and liveness detection.
On Simulator, stubs are used automatically. This is by design.
If you add the frameworks but forget to set SMART_CAPTURE_ENABLED in Active Compilation Conditions, the app compiles and runs — but silently uses stubs. There is no error. Check Build Settings if the Smart Capture views are not appearing.
Even with SMART_CAPTURE_ENABLED set, the Simulator always uses stubs. The #if !targetEnvironment(simulator) condition ensures this. Smart Capture SDKs require real camera hardware.
All four frameworks must be set to Embed & Sign in the target’s Frameworks, Libraries, and Embedded Content. “Embed Without Signing” or “Do Not Embed” causes runtime crashes.