Capture-Only mode presents Incode's capture UI and returns the captured media to your app without uploading that media to Incode for server-side processing. Use this mode when your app needs to control when and how captures are sent to your own back end.
The SDK still performs the local checks needed to guide capture, such as framing and image-quality checks. Server-side validation, OCR, face matching, and other processing do not run as part of the Capture-Only section.
Info
Capture-Only mode is not available in the Public distribution variant. If you set sdkMode to .captureOnly in that variant, the SDK logs a warning and uses .standard mode instead.
Supported Modules
Capture-Only mode supports these capture modules:
| Module | Builder | Result callback |
|---|---|---|
| ID Capture | addIdScan(...) |
onIdFrontCompleted(_:) and onIdBackCompleted(_:) |
| Selfie | addSelfieScan(...) |
onSelfieScanCompleted(_:) |
| Document Capture | addDocumentScan(...) |
onDocumentScanCompleted(_:) |
| Video Selfie | addVideoSelfie(videoSelfieConfiguration:) |
onVideoSelfieCompleted(_:) |
Do not add processing or non-capture modules to a Capture-Only flow. Note that:
addIdScan(scanStep: .both)normally runs Process ID automatically after ID Capture. In this mode, it does not; it adds ID Capture only.- The Process ID and NFC modules are not supported when
sdkModeis.captureOnly. - You can add the Intro module, but it shows a local screen only; it returns no capture result.
- Capture-Only mode does not support the Full Name, Phone, Email, Geolocation, or Signature modules.
Set sdkMode before creating an IncdOnboardingFlowConfiguration. Some builder behavior depends on the active SDK mode.
How It Works
Use the Sections API to run one capture section at a time. A module-specific callback returns the capture result. After the section closes, onOnboardingSectionCompleted(_:) reports its tag and the next section can start.
sequenceDiagram
autonumber
participant App as Host App
participant SDK as Incode SDK
participant Server as Customer Server
App->>SDK: Initialize and set sdkMode = .captureOnly
App->>SDK: startOnboardingSection(...)
SDK->>SDK: Local checks and capture
SDK-->>App: Module result callback
SDK-->>App: onOnboardingSectionCompleted(sectionTag)
App->>Server: Send captured media and metadata
Server-->>App: Accept or request another capture
App->>SDK: Start the next section when needed
App->>SDK: finishFlow(...)
SDK-->>App: finishFlow completion
Note over App,SDK: Diagnostics delivery is asynchronous and<br/>is not ordered with the completion
SDK-->>App: onDiagnosticsDataReceived(data), when enabled and available
App->>SDK: deleteLocalUserData()
Only one section can run at a time. Starting another section before the active one completes reports IncdFlowError.sectionAlreadyRunning(activeSectionTag) to the delegate supplied for the rejected request. The active section continues running.
onOnboardingSectionCompleted(_:) is the safe point to start another section. Do not start the next section from a module result callback.
Set Up Capture-Only Mode
1. Initialize the SDK
Set the required presenting view controller, initialize the SDK, and then enable Capture-Only mode. The SDK manager is a singleton; the object that must remain available for presentation is your view controller because presentingViewController is a weak reference.
let manager = IncdOnboardingManager.shared
manager.presentingViewController = self
manager.initIncdOnboarding { success, _ in
guard success == true else {
// Handle IncdInitError.
return
}
manager.sdkMode = .captureOnly
}
All initIncdOnboarding(...) configuration parameters are optional. Pass your normal url, apiKey, logging, or experiment settings when your integration requires them. Console logging is disabled by default; set loggingEnabled: true only when you need SDK console logs.
2. Build and Start a Capture Section
Create a separate IncdOnboardingFlowConfiguration for each section and pass your IncdOnboardingDelegate to startOnboardingSection(flowConfig:sectionTag:delegate:).
ID Capture
let flow = IncdOnboardingFlowConfiguration()
flow.addIdScan(scanStep: .both)
IncdOnboardingManager.shared.startOnboardingSection(
flowConfig: flow,
sectionTag: "id-capture",
delegate: self
)
For .both, the same section captures the front and then the back of an ID. It calls onIdFrontCompleted(_:), then onIdBackCompleted(_:), and finally calls onOnboardingSectionCompleted("id-capture"). A passport has no back capture.
Key IdScanResult capture fields are:
image: UIImage?base64Image: String?encryptedBase64Image: String?chosenIdType: IdType?idCategory: IDCategorymetadata: String?, populated for Capture-Only captureserror: IncdIdScanError?
See ID Capture for all configuration options and result fields.
Selfie
let flow = IncdOnboardingFlowConfiguration()
flow.addSelfieScan()
IncdOnboardingManager.shared.startOnboardingSection(
flowConfig: flow,
sectionTag: "selfie",
delegate: self
)
Key SelfieScanResult capture fields are:
image: UIImage?selfieBase64: String?selfieEncryptedBase64: String?metadata: String?frameWithDepth: String?, when that capture option is enabled and supportedvideoFileURL: URL?andpresignedVideoFileURL: URL?, when video capture is enablederror: SelfieScanError?
Read URL-backed results before deleting local user data. See Selfie for all configuration options and result fields.
Document Capture
let flow = IncdOnboardingFlowConfiguration()
flow.addDocumentScan(
documentType: .addressStatement,
documentSources: [.camera, .photoUpload, .fileUpload]
)
IncdOnboardingManager.shared.startOnboardingSection(
flowConfig: flow,
sectionTag: "document-capture",
delegate: self
)
Key DocumentScanResult capture fields are:
documentImage: UIImage?data: Data?, containing image or PDF bytes when availablemimeType: String?documentType: DocumentTypeerror: DocumentScanError?
Server-generated OCR result fields are not produced by a Capture-Only section. See Document Capture for all configuration options and result fields.
Video Selfie
let configuration = VideoSelfieConfiguration()
configuration.tutorials(enabled: true)
configuration.selfieScan(enabled: true)
configuration.idScan(enabled: true, validateId: false)
let flow = IncdOnboardingFlowConfiguration()
flow.addVideoSelfie(videoSelfieConfiguration: configuration)
IncdOnboardingManager.shared.startOnboardingSection(
flowConfig: flow,
sectionTag: "video-selfie",
delegate: self
)
VideoSelfieResult can contain:
selfie: UIImage?idFront: UIImage?,idBack: UIImage?, andpassport: UIImage?document: UIImage?videoData: Data?andaudioData: Data?voiceConsentSelfie: UIImage?error: VideoSelfieError?
The SDK delivers onVideoSelfieCompleted(_:) when the result contains recorded video or audio. A fatal Video Selfie failure is also reported through onError(_:). See Video Selfie for all configuration options and result fields.
Receive Results
Implement the callbacks for the modules you use. Copy or upload any result data your app needs before local cleanup.
extension MyCaptureViewController: IncdOnboardingDelegate {
func onIdFrontCompleted(_ result: IdScanResult) {
// Store or upload the front image and metadata.
}
func onIdBackCompleted(_ result: IdScanResult) {
// Store or upload the back image and metadata.
}
func onSelfieScanCompleted(_ result: SelfieScanResult) {
// Store or upload the selfie, metadata, and optional video data.
}
func onDocumentScanCompleted(_ result: DocumentScanResult) {
// Store or upload result.data and result.mimeType.
}
func onVideoSelfieCompleted(_ result: VideoSelfieResult) {
// Store or upload the captured Video Selfie assets.
}
func onOnboardingSectionCompleted(_ flowTag: String) {
// The section is closed. Start the next section when your app is ready.
}
func onSuccess() {
// Successful Sections API cleanup is reported by finishFlow's completion.
}
func onError(_ error: IncdFlowError) {
if case .sectionAlreadyRunning = error {
// The original section is still active. Do not delete its local data.
return
}
// Persist any capture data you need, then remove SDK-local user data.
IncdOnboardingManager.shared.deleteLocalUserData()
}
func userCancelledSession() {
IncdOnboardingManager.shared.deleteLocalUserData()
}
}
Forward Capture Metadata
When you submit ID or Selfie captures from your back end, include the corresponding metadata value from IdScanResult or SelfieScanResult. Forward it unchanged with the capture it describes. This metadata is part of the server-side capture-processing contract.
Finish the Capture-Only Session
After the final section completes successfully and your app has retained the capture data it needs, call finishFlow once:
IncdOnboardingManager.shared.finishFlow { success, error in
guard success else {
// Handle IncdError.
return
}
IncdOnboardingManager.shared.deleteLocalUserData()
}
In Capture-Only mode, finishFlow performs local flow cleanup and does not call the back end. For a successful Sections API integration, its completion is the success signal; it does not call onSuccess().
Forward Diagnostics Data
When diagnostics are enabled, the SDK returns a prepared diagnostics payload through onDiagnosticsDataReceived(_:) instead of uploading it. The callback can arrive asynchronously after finishFlow completes. Terminal SDK errors and user cancellation also finalize diagnostics automatically.
func onDiagnosticsDataReceived(_ data: Data) {
// Forward the prepared payload without modifying it.
}
Use the diagnostics upload contract provided for your Incode server integration. If sendDiagnosticsData is false, the SDK does not produce this callback.
Delete Local User Data
deleteLocalUserData() clears the current onboarding state and removes SDK-managed local capture files. Call it only after your app has copied or uploaded every capture it needs.
Use these terminal cleanup points:
- The
finishFlowcompletion after a successful Capture-Only Sections flow. onError(_:)for a terminal flow error, excluding.sectionAlreadyRunning.userCancelledSession()after cancellation.
Danger
Do not call deleteLocalUserData() while a section is active or before reading URL-backed capture results. Deleting local files is permanent.
Customization
Capture-Only mode uses the same theme, localization, tutorial, retake, and capture-quality options as the corresponding modules in other SDK modes. See Customization and each supported module page for the available settings.
For related integration guidance, see SDK Modes, Integration Approaches, and API Reference.