> ## Documentation Index
> Fetch the complete documentation index at: https://docs.didit.me/llms.txt
> Use this file to discover all available pages before exploring further.

# iOS 身份验证 SDK：Didit 移动端 KYC 原生集成

> Didit 提供原生 iOS Swift SDK，支持身份验证、生物活体检测与 NFC 电子护照读取。按量计费，每月免费 $10，无需签约。



iOS SDK 集成提示
        将提示词复制到剪贴板



      
      

\<pre>

\`Integrate Didit identity verification into my iOS app using the native Swift SDK (DiditSDK 4.13.2).

\## Requirements
\- iOS 13.0+, Xcode 15.0+, Swift 5.9+
\- NFC chip reading requires iOS 15+ and a compatible device with NFC hardware
\- Since 4.0.0 the SDK ships in four variants; pick exactly one:
\- Core (DiditSDKCore): no NFC, no auto-detection, no OpenSSL, iOS 13.0+, smallest binary (manual capture only)
\- AutoDetection (DiditSDKAutoDetection): ML auto-capture, no NFC, no OpenSSL, iOS 13.0+
\- NFC (DiditSDKNFC): NFC passport reading, bundles OpenSSL 3.3.3, iOS 15.0+
\- All (DiditSDK — default): every feature, bundles OpenSSL 3.3.3, iOS 15.0+

\## Installation

\### Option A: Swift Package Manager (recommended)
1\. In Xcode: File > Add Package Dependencies
2\. Enter URL: https://github.com/didit-protocol/sdk-ios.git
3\. Select version 4.13.2 (or "Up to Next Major") and click Add Package
4\. Add exactly one library product for your variant:
\- All (NFC + auto-detection): product "DiditSDK"
\- Core (no NFC, no auto-detection): product "DiditSDKCore"
\- AutoDetection (auto-capture, no NFC): product "DiditSDKAutoDetection"
\- NFC (NFC, no auto-detection): product "DiditSDKNFC"

In Package.swift:
.package(url: "https://github.com/didit-protocol/sdk-ios.git", from: "4.13.2")
Then add one product, e.g. .product(name: "DiditSDK", package: "DiditSDK") or .product(name: "DiditSDKCore", package: "DiditSDK"). Your code always writes import DiditSDK regardless of variant.

\### Option B: CocoaPods (binary podspec — DiditSDK is not on the public Trunk)
podspec_url = 'https://raw.githubusercontent.com/didit-protocol/sdk-ios/4.13.2/DiditSDK.podspec'
\# Pick exactly one subspec:
\# All (NFC + auto-detection, iOS 15+):
platform :ios, '15.0'
pod 'DiditSDK/All', :podspec => podspec_url
\# Core (no NFC, no auto-detection, iOS 13+):        pod 'DiditSDK/Core', :podspec => podspec_url
\# AutoDetection (auto-capture, no NFC, iOS 13+):    pod 'DiditSDK/AutoDetection', :podspec => podspec_url
\# NFC (NFC, no auto-detection, iOS 15+):            pod 'DiditSDK/NFC', :podspec => podspec_url

Then run: pod install
Open the .xcworkspace (not .xcodeproj).

On Xcode 15+ if you hit rsync "Operation not permitted", set Build Setting ENABLE_USER_SCRIPT_SANDBOXING to No on the project (Debug and Release).

\## Required Permissions (Info.plist)
\- NSCameraUsageDescription (required) — Document scanning and face verification
\- NSMicrophoneUsageDescription (required) — Video recording for liveness checks
\- NSPhotoLibraryUsageDescription (required) — Upload documents from gallery
\- NFCReaderUsageDescription (only if using the NFC or All variant) — Read NFC chips in passports/IDs
\- NSLocationWhenInUseUsageDescription (optional) — Geolocation for fraud prevention

\## NFC Configuration (NFC or All variant only — skip for the Core or AutoDetection variants)
1\. Add "Near Field Communication Tag Reading" capability in Xcode > Signing & Capabilities
2\. Add ISO7816 identifiers to Info.plist:
com.apple.developer.nfc.readersession.iso7816.select-identifiers:
\["D23300000045737445494420763335", "A0000002471001", "A0000002472001", "00000000000000", "D392F000260100000001"]
(D392F000260100000001 is the Japan My Number Card's JPKI application: without it, iOS does not report that card to the SDK.)
3\. Add to entitlements file:
com.apple.developer.nfc.readersession.formats: ["TAG"]

\## Backend: Create Session (your server)
POST https://verification.didit.me/v3/session/
Headers: { "x-api-key": DIDIT_API_KEY, "Content-Type": "application/json" }
Body: { "workflow_id": DIDIT_WORKFLOW_ID, "vendor_data": "user-id" }
Response: { "session_id": "uuid", "session_token": "short-token", "url": "https://verify.didit.me/session/short-token", "status": "Not Started" }
Send session_token to the iOS app (never expose DIDIT_API_KEY to the device).

\## SwiftUI Integration (recommended)
import SwiftUI
import DiditSDK

struct ContentView: View {
var body: some View {
    Button("Verify Identity") {
        // Option A: With session token from your backend (recommended)
        DiditSdk.shared.startVerification(token: "session-token-from-backend")

        // Option B: With workflow ID (SDK creates session internally — UniLink, vendorData only)
        // DiditSdk.shared.startVerification(workflowId: "your-workflow-id", vendorData: "user-id")
    }
    .diditVerification { result in
        switch result {
        case .completed(let session):
            // session.status: VerificationStatus = .approved | .pending | .declined
            if session.status == .approved { /* grant access */ }
        case .cancelled(let session):
            // User dismissed the flow; session may be nil if none was created
            \_ = session
        case .failed(let error, _):
            // VerificationError — show error.localizedDescription
            \_ = error
        }
    }
}
}

\## Programmatically dismissing the verification (3.6.0+)
Call DiditSdk.shared.dismiss() to end an active verification (e.g. on app backgrounding).
It dismisses the UI, resets state, and fires the .diditVerification handler with .cancelled(session:).
Do NOT toggle DiditSdk.shared.isPresented = false or call UIKit dismiss() on the topmost VC — that bypasses the completion pipeline.

\## Result Statuses (SDK enum / API string)
\- .approved / "Approved"  — user identity verified, grant access
\- .declined / "Declined"  — verification failed, show retry or contact support
\- .pending  / "In Review" — needs manual review by your compliance team

\## Full Verification Results
The SDK only returns the status. Full decision data (document fields, face match scores, AML hits, etc.) arrives via webhook to your backend.
Set up webhooks: https://docs.didit.me/integration/webhooks

\## Rate Limits (session creation)
\- 600 session-create requests / minute per x-api-key (see https://docs.didit.me/integration/rate-limiting)
\- On 429, retry honouring Retry-After / X-RateLimit-* headers

\## Environment Variables (backend only — never expose in the iOS app)
\- DIDIT_API_KEY — from Didit Console > API & Webhooks
\- DIDIT_WORKFLOW_ID — from Didit Console > Workflows

\## App Store Note
If you ship the NFC or All variant, Apple may request an NFC demo video during review. Request ours from [support@didit.me](mailto:support@didit.me).

\## Docs
\- iOS SDK: https://docs.didit.me/integration/native-sdks/ios-sdk
\- GitHub: https://github.com/didit-protocol/sdk-ios
\- Webhooks: https://docs.didit.me/integration/webhooks
\- API Reference: https://docs.didit.me/api-reference/overview`

\</pre>



      
      

粘贴到

一款轻量级、由服务端驱动的 iOS SDK，用于身份验证，只需极少配置。**最新版本：4.13.2。**> [card] [GitHub 代码仓库](https://github.com/didit-protocol/sdk-ios)
>
> 在 GitHub 上查看源代码、版本发布和完整更新日志> [!NOTE]
>
> ### **原生 SDK**: 这是 iOS 应用推荐的集成方式。原生 SDK 提供最佳的用户体验、经过优化的相机处理，并完整支持 NFC。***## 要求| 要求 | 最低版本 |
| - | - |
| iOS | 13.0+ |
| Xcode | 15.0+ |
| Swift | 5.9+ |### iOS 版本兼容性| iOS 版本 | 可用功能 |
| - | - |
| iOS 13.0 - 14.x | **除**NFC 护照读取外的所有功能 |
| iOS 15.0+ | 所有功能，包括 NFC 护照读取 |> [!NOTE]
>
> **生物识别同意需要原生 SDK 4.10.0 或更高版本。** 对于受伊利诺伊州、得克萨斯州或科罗拉多州法律约束的人员，Didit 可以在首次人脸采集前请求明确的[生物识别同意](https://docs.didit.me/zh/getting-started/biometric-consent.md)。iOS SDK 4.10.0 及更高版本会显示该步骤。早期版本无法显示该步骤：其会话仍按原方式运行，决策结果会将同意报告为未收集（`biometric_consent.reason: "BIOMETRIC_CONSENT_UNSUPPORTED_SDK"`）。如果您在这些州验证人员，请升级。***## 安装自 `4.0.0` 起，SDK 提供多个可安装的变体，因此你的应用只需承担所用功能对应的二进制体积。请恰好选择一个变体，即一个 CocoaPods 子规范和一个 SwiftPM 库产品：| 变体 | CocoaPods | SwiftPM 产品 | NFC | 自动检测 | OpenSSL | 最低 iOS |
| - | - | - | :-: | :-: | - | - |
| **Core** | `DiditSDK/Core` | `DiditSDKCore` | – | ✓ | 无 | iOS 13.0+ |
| **AutoDetection**（已弃用） | `DiditSDK/AutoDetection` | `DiditSDKAutoDetection` | – | ✓ | 无 | iOS 13.0+ |
| **NFC** | `DiditSDK/NFC` | `DiditSDKNFC` | ✓ | ✓ | 内置 `3.3.3` | iOS 15.0+ |
| **All**（默认） | `DiditSDK/All` | `DiditSDK` | ✓ | ✓ | 内置 `3.3.3` | iOS 15.0+ |- **Core**：体积最小，通过 Didit 自有的轻量检测器自动完成证件和自拍照采集。不包含 `OpenSSL.xcframework`，也没有 CoreNFC 运行时。
- **AutoDetection**：自 4.11.0 起已弃用，与 **Core** 完全相同，请改用 **Core**。
- **NFC**：在 **Core** 的基础上增加 NFC 护照和电子身份证芯片读取。
- **All**：包含全部功能。安装 `DiditSDK` 且未指定子规范或产品名时，默认使用此变体。各变体会为你的应用包增加以下体积（arm64 Mach-O，以 `4.9.0` 为基准测量）：**Core** 约 11.1 MB，**NFC** 约 11.7 MB 加上约 4 MB 的 OpenSSL，**AutoDetection** 约 16.0 MB，**All** 约 16.6 MB 加上 OpenSSL。
检测模型本身在运行时下载并缓存，因此不计入应用的下载体积。自 `4.11.0` 起，所有变体的自动采集都运行在 Didit 自有的轻量检测器上。TensorFlow Lite 不再是 SDK 的一部分，框架体积也因此变小，因为其翻译文件改为从二进制文件之外加载。自 `4.6.0` 起，已发布的框架不再包含 Swift ABI 描述符（Xcode 会将其复制到每个应用中，但从不读取的构建元数据），也不再包含该变体无法播放的教程视频。从 `4.5.4` 或更早版本升级，仅此一项即可使应用缩小约 7-8 MB，无需修改代码。
App Store 在分发时会压缩二进制文件，因此空中下载的实际开销更低。每台设备的具体差异，请查看 App Store Connect 中的应用体积报告。> [!NOTE]
>
> **NFC** 和 **All** 变体内置 `OpenSSL.xcframework`（**OpenSSL 3.3.3**，通过 `NFCPassportReader` 以 `OpenSSL-Universal 3.3.3001` 形式分发），它是 NFC 芯片认证所必需的，并且需要 iOS 15.0+。**截至 4.0.6（含）的版本**内置的是 OpenSSL `1.1.1s`；**4.0.7 及更高版本**内置 OpenSSL 3.3.3，并附带自己的隐私清单和代码签名。请务必安装 **4.0.7 或更高版本**。注意，升级只能统一 OpenSSL *版本*，无法消除 *嵌入* 冲突：如果你应用中的其他依赖也嵌入了 `OpenSSL.framework`（文件名相同，`com.github.krzyzanowskim.OpenSSL` Bundle ID 也相同），那么无论版本如何，两份副本都会在嵌入步骤中冲突。只保留一份：例如，按 SPM 标识覆盖另一个包，改用 Didit 发布的 `OpenSSL.xcframework`，以确保包内的二进制文件与本 SDK 构建时所用的一致。**Core** 和 **AutoDetection** 变体完全不链接任何 OpenSSL。无论安装哪个变体，应用代码中的 `import DiditSDK` 都相同。### Swift Package Manager (推荐)使用 Xcode 将该包添加到你的项目中：1) 前往 **File > Add Package Dependencies**
2) 输入代码仓库 URL：```
https://github.com/didit-protocol/sdk-ios.git
```3) 选择版本（或从 `4.13.2` 起选择 **Up to Next Major**），然后点击 **Add Package**
4) 出现选择产品的提示时，为你的变体恰好选择一个库产品（参见上方表格）或者在你的 `Package.swift` 中添加：```swift theme={null}
dependencies: [
    .package(url: "https://github.com/didit-protocol/sdk-ios.git", from: "4.13.2")
]
```然后将以下产品中恰好一个添加到你的目标中：```swift theme={null}
// All (default — NFC + auto-detection)
.product(name: "DiditSDK", package: "DiditSDK")

// Core (no NFC, no auto-detection, no OpenSSL)
.product(name: "DiditSDKCore", package: "DiditSDK")

// AutoDetection (auto-capture, no NFC)
.product(name: "DiditSDKAutoDetection", package: "DiditSDK")

// NFC (NFC passport reading, no auto-detection)
.product(name: "DiditSDKNFC", package: "DiditSDK")
```> [!WARNING]
>
> **Swift Package Manager 不会替你强制执行 iOS 15 的下限。** `Package.swift` 为整个包声明了
> `platforms: [.iOS(.v13)]`，因此 Xcode 会允许你将 `DiditSDK` 或 `DiditSDKNFC` 添加到 iOS 13 或 14 的目标中且不报错，但这两个产品都内置 `OpenSSL.xcframework`，并通过 `NFCPassportReader` 读取芯片，它们需要 **iOS 15.0+**。使用 `All` 或 `NFC` 产品时，请自行将目标的部署目标设为 15.0。CocoaPods 会通过子规范的 `deployment_target` 自动强制执行这一要求。### CocoaPods`DiditSDK` 以二进制 podspec 的形式托管在代码仓库中（它不在公共 CocoaPods Trunk 上），因此你必须引用 podspec 的 URL。请恰好选择一个子规范：```ruby theme={null}
podspec_url = 'https://raw.githubusercontent.com/didit-protocol/sdk-ios/4.13.2/DiditSDK.podspec'

# All (NFC + auto-detection, iOS 15.0+):
platform :ios, '15.0'
pod 'DiditSDK/All', :podspec => podspec_url

# Core (no NFC, no auto-detection, iOS 13.0+):
# pod 'DiditSDK/Core', :podspec => podspec_url

# AutoDetection (auto-capture, no NFC, iOS 13.0+):
# pod 'DiditSDK/AutoDetection', :podspec => podspec_url

# NFC (NFC passport reading, iOS 15.0+):
# pod 'DiditSDK/NFC', :podspec => podspec_url
````Core` 和 `AutoDetection` 可部署到 iOS 13；`NFC` 和 `All` 因 `NFCPassportReader` 需要 iOS 15。然后运行：```bash theme={null}
pod install
```> [!NOTE]
>
> 执行 `pod install` 后，请打开生成的 `.xcworkspace`（而不是 `.xcodeproj`）。> [!NOTE]
>
> **Xcode 15+ rsync 错误**：如果在构建时看到 `Operation not permitted` rsync 错误，请在项目的 Debug 和 Release 配置中，将 **Build Settings → User Script Sandboxing**（`ENABLE_USER_SCRIPT_SANDBOXING`）设置为 **否**。### 手动安装 (XCFramework)每个版本为每个变体各发布一个 XCFramework。从 [GitHub Releases](https://github.com/didit-protocol/sdk-ios/releases) 页面下载你所用变体的压缩包，然后将 `.xcframework` 文件夹拖入 Xcode，并将其设置为 **Embed & Sign**。| 变体 | 下载文件 |
| - | - |
| **Core** | `DiditSDK-Core.xcframework.zip` |
| **AutoDetection** | `DiditSDK-AutoDetection.xcframework.zip` |
| **NFC** | `DiditSDK-NFC.xcframework.zip` + `OpenSSL.xcframework.zip` |
| **All** | `DiditSDK.xcframework.zip` + `OpenSSL.xcframework.zip` |***## PermissionsSDK 需要以下权限。请将这些配置添加到应用的 `Info.plist` 中：| 权限 | Info.plist 键 | 说明 | 是否必需 |
| - | - | - | - |
| 相机 | `NSCameraUsageDescription` | 证件扫描与人脸验证 | ✅ 是 |
| 麦克风 | `NSMicrophoneUsageDescription` | 活体检测的视频录制 | ✅ 是 |
| 相册 | `NSPhotoLibraryUsageDescription` | 从设备相册上传证件 | ✅ 是 |
| NFC | `NFCReaderUsageDescription` | 读取护照或身份证中的 NFC 芯片 | ⚠️ 仅 Full SDK |
| 位置 | `NSLocationWhenInUseUsageDescription` | 用于防欺诈的地理位置 | ❌ 可选 |### Info.plist 配置示例```xml theme={null}
<key>NSCameraUsageDescription</key>
<string>Camera access is required to scan your identity documents for verification.</string>

<key>NSMicrophoneUsageDescription</key>
<string>Microphone access is required to record video for liveness verification.</string>

<key>NSPhotoLibraryUsageDescription</key>
<string>Photo library access is required to upload document images.</string>

<key>NFCReaderUsageDescription</key>
<string>NFC access is required to read the chip in your identity document.</string>

<key>NSLocationWhenInUseUsageDescription</key>
<string>Location access helps verify your identity and prevent fraud.</string>
```### NFC 配置仅在安装 **NFC** 或 **All** 变体时需要。如果您安装的是 **Core** 或 **AutoDetection** 变体，请跳过本节。要为带芯片的护照和身份证启用 NFC 读取，请按以下步骤操作：1) **在 Xcode 中添加 NFC 功能**：
   * 选择您的目标 → **Signing & Capabilities** → **+ Capability** → **Near Field Communication Tag Reading**

2) **在 `Info.plist` 中添加 ISO7816 标识符**：```xml theme={null}
<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
<array>
    <string>D23300000045737445494420763335</string>
    <string>A0000002471001</string>
    <string>A0000002472001</string>
    <string>00000000000000</string>
    <string>D392F000260100000001</string>
</array>
````D392F000260100000001` 可让 SDK 通过 [JPKI](https://docs.didit.me/zh/core-technology/nfc-verification/overview.md#日本个人编号卡（jpki）) 读取日本 My Number 卡。若不添加该标识符，iOS 不会将此卡报告给 SDK。My Number 卡读取功能尚未在生产环境中启用，该标识符对其他证件没有任何影响，因此您现在即可添加。3) **添加 Entitlements**（在您的 `.entitlements` 文件中）：```xml theme={null}
<key>com.apple.developer.nfc.readersession.formats</key>
<array>
    <string>TAG</string>
</array>
```> [!WARNING]
>
> **模拟器限制（NFC 和 All 变体）**：这些变体会链接 CoreNFC，而自 Xcode 12 起，模拟器中缺少 `libnfshared.dylib`。变通方法请参阅[这个 Stack Overflow 帖子](https://stackoverflow.com/questions/63915728/xcode12-corenfc-simulator-library-not-loaded)。此限制**不**适用于 **Core** 或 **AutoDetection** 变体。请仅在实体设备上测试 NFC 功能。> [!NOTE]
>
> ### **App Store 审核（NFC 和 All 变体）**：如果您安装的是 **NFC** 或 **All** 变体，即使您的工作流不使用 NFC，Apple 在审核时也可能要求提供演示视频，因为 SDK 二进制文件中包含 NFC 相关代码。请发送邮件至 [support@didit.me](mailto:support@didit.me) 索取我们的 NFC 演示视频，用于提交给 Apple。此要求不适用于 **Core** 或 **AutoDetection** 变体。***## 快速开始### SwiftUI 集成```swift theme={null}
import SwiftUI
import DiditSDK

struct ContentView: View {
    var body: some View {
        Button("Verify Identity") {
            // Method 1: UniLink — no backend required
            DiditSdk.shared.startVerification(workflowId: "your-workflow-id")
            
            // Method 2: Backend Session — full parameter control
            // DiditSdk.shared.startVerification(token: "your-session-token")
        }
        .diditVerification { result in
            handleResult(result)
        }
    }
    
    private func handleResult(_ result: VerificationResult) {
        switch result {
        case .completed(let session):
            print("Completed: \(session.status)")
        case .cancelled(let session):
            print("Cancelled")
        case .failed(let error, _):
            print("Failed: \(error.localizedDescription)")
        }
    }
}
```### UIKit 集成```swift theme={null}
import UIKit
import DiditSDK

class VerificationViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Start verification when ready
        DiditSdk.shared.startVerification(token: "your-session-token")
    }
}
```***## 集成方式SDK 支持两种集成方式：### 方式一：UniLink（最简单）无需后端。SDK 使用您在 [Didit 控制台](https://business.didit.me)中获取的工作流 ID 直接创建会话。UniLink 方式（`startVerification(workflowId:)`）仅支持 `vendorData`；如需其他会话参数，请使用方式二。> [!NOTE]
>
> 要通过 UniLink 将 `vendorData` 附加到会话，请在 [Didit 控制台](https://business.didit.me)中启用 **供应商数据** 选项。```swift theme={null}
DiditSdk.shared.startVerification(
    workflowId: "your-workflow-id",
    vendorData: "user-123"
)
```### 方式二：后端会话（生产环境推荐）您的后端通过[创建验证会话 API](https://docs.didit.me/zh/sessions-api/create-session.md)（`POST /v3/session/`）创建会话，该接口支持全部参数（`contact_details`、`expected_details`、`metadata`、`callback` 等），然后将 `session_token` 传递给 SDK。```swift theme={null}
// Your backend creates a session and returns the token
let sessionToken = await yourBackend.createVerificationSession(userId: currentUser.id)

// Pass the token to the SDK
DiditSdk.shared.startVerification(token: sessionToken)
```这种方式让您可以完全控制以下内容：- 将会话与您的用户关联（`vendor_data`）
- 设置联系方式和预期信息，用于交叉验证
- 设置自定义元数据
- 为每个会话配置回调以上数据（联系方式、预期信息、元数据、回调）会发送至[创建验证会话 API](https://docs.didit.me/zh/sessions-api/create-session.md)。***## 配置使用 `DiditSdk.Configuration` 自定义 SDK 的行为：```swift theme={null}
let configuration = DiditSdk.Configuration(
    languageLocale: .spanish,      // Force Spanish language
    showLanguageSelector: true,    // Let the user change language
    fontFamily: "Avenir",          // Custom font (must be registered in your app)
    loggingEnabled: true,          // Enable debug logging
    showCloseButton: true,         // Show close (X) button on step screens
    showExitConfirmation: true,    // Show confirmation dialog when user taps close
    closeOnComplete: false         // Don't auto-dismiss on completion
)

DiditSdk.shared.startVerification(
    token: "your-session-token",
    configuration: configuration
)
```### 配置选项| 属性 | 类型 | 默认值 | 描述 |
| - | - | - | - |
| `languageLocale` | `SupportedLanguage?` | 设备区域设置 | 强制使用指定语言 |
| `showLanguageSelector` | `Bool` | `false` | 在欢迎页面上显示网页风格的语言选择器 |
| `fontFamily` | `String?` | 系统字体 | 自定义字体族名称（必须通过 `Info.plist` 中的 `UIAppFonts` 在应用中注册） |
| `loggingEnabled` | `Bool` | `false` | 启用 SDK 调试日志 |
| `showCloseButton` | `Bool` | `true` | 在验证步骤页面上显示关闭（X）按钮 |
| `showExitConfirmation` | `Bool` | `true` | 用户尝试退出时显示确认对话框 |
| `closeOnComplete` | `Bool` | `false` | 完成后自动关闭验证界面（Web SDK 对应选项：`closeModalOnComplete`） |
| `defaultDocumentCamera` | `CameraLens` | `.back` | 证件采集页面打开时使用的镜头 |
| `defaultLivenessCamera` | `CameraLens` | `.front` | 人脸（活体检测）采集页面打开时使用的镜头 |
| `showDocumentCameraSwitchButton` | `Bool` | `true` | 在证件采集页面上显示采集过程中的摄像头切换按钮 |
| `showLivenessCameraSwitchButton` | `Bool` | `true` | 在人脸采集页面上显示采集过程中的摄像头切换按钮 |### 摄像头选项摄像头选项与 [Web SDK 摄像头选项](https://docs.didit.me/zh/integration/web-sdks/javascript-sdk.md#摄像头选项)一致。
每个镜头为 `.front` 或 `.back`。
如果设备没有所请求的镜头，SDK 会使用设备上的第一个摄像头。
在只有一个摄像头的设备上，摄像头切换按钮也会隐藏。
将切换按钮选项设为 `false`，即可将用户锁定在该步骤的默认镜头上。```swift theme={null}
let configuration = DiditSdk.Configuration(
    defaultLivenessCamera: .back,
    showLivenessCameraSwitchButton: false
)
```> [!NOTE]
>
> **主题与颜色**：颜色、背景和引导页设置需要在 Didit Console 中通过您的[白标设置](https://business.didit.me)进行配置，而不是在 SDK 配置中设置。这可确保各平台品牌一致。> [!NOTE]
>
> 选项 `showLanguageSelector`、`showCloseButton`、`showExitConfirmation` 和 `closeOnComplete` 与 Web SDK 的 `DiditSdkConfiguration` 一致。移动端专属的 `languageLocale` 和 `fontFamily` 选项之所以存在，是因为移动端 SDK 以原生方式渲染完整的验证界面（不同于 Web SDK，后者在 iframe 中交由托管前端处理）。***## 语言支持SDK 支持 **54 种语言**。若未指定语言，SDK 会使用设备区域设置，并以英语作为回退语言。```swift theme={null}
// Use device locale (default)
let config = DiditSdk.Configuration()

// Force specific language
let config = DiditSdk.Configuration(languageLocale: .french)

// Let the user change language on the welcome screen
let config = DiditSdk.Configuration(showLanguageSelector: true)

// Detect device locale programmatically
let deviceLanguage = SupportedLanguage.fromDeviceLocale()
```**[查看所有支持的语言 →](https://docs.didit.me/zh/integration/supported-languages.md)*****## 高级会话参数如需使用高级会话参数（`contact_details`、`expected_details`、`metadata`、`callback`），请使用 **Backend Session** 方式。您的后端需调用[创建验证会话 API](https://docs.didit.me/zh/sessions-api/create-session.md) 并传入完整参数，然后将 `session_token` 传递给 SDK。***## 处理结果`VerificationResult` 枚举提供验证的结果：### 结果分支| 分支 | 描述 |
| - | - |
| `.completed(session:)` | 验证流程已完成（结果请查看 `session.status`） |
| `.cancelled(session:)` | 用户取消了验证流程 |
| `.failed(error:session:)` | 验证过程中发生错误 |### SessionData 属性| 属性 | 类型 | 描述 |
| - | - | - |
| `sessionId` | `String` | 唯一会话标识符 |
| `status` | `VerificationStatus` | `.approved`、`.pending` 或 `.declined` |
| `country` | `String?` | 国家/地区代码（ISO 3166-1 alpha-3） |
| `documentType` | `String?` | 用于验证的证件类型 |### 错误类型| 错误 | 描述 |
| - | - |
| `.sessionExpired` | 会话已过期 |
| `.networkError` | 网络连接问题 |
| `.cameraAccessDenied` | 未授予摄像头权限 |
| `.unknown(String)` | 其他错误及其消息 |### 连接中断如果设备在验证过程中断开连接，SDK 会在流程中显示“无网络连接”页面，而不是让步骤一直加载。
SDK 会在后台持续检测，一旦 Didit 恢复可访问，便会自动关闭该页面，用户会从所在的步骤继续操作。
该页面提供 **Try Again** 按钮；当 `showCloseButton` 为 `true`（默认值）时，还会提供 **Exit Verification** 按钮。
退出会以 `.failed(error: .networkError, session:)` 结束验证，使您的应用得知用户是因连接问题而离开的。### 完整结果处理示例```swift theme={null}
.diditVerification { result in
    switch result {
    case .completed(let session):
        switch session.status {
        case .approved:
            print("✅ Approved! Session: \(session.sessionId)")
            // User is verified - grant access
            
        case .pending:
            print("⏳ Under review. Session: \(session.sessionId)")
            // Show "verification in progress" UI
            
        case .declined:
            print("❌ Declined. Session: \(session.sessionId)")
            // Handle declined verification
        }
        
    case .cancelled(let session):
        if let session = session {
            print("🚫 Cancelled session: \(session.sessionId)")
        }
        // User chose to cancel - maybe show retry option
        
    case .failed(let error, let session):
        print("⚠️ Error: \(error.localizedDescription)")
        // Handle error - show retry or contact support
    }
}
```***## 以编程方式关闭验证> [!NOTE]
>
> 在 DiditSDK **3.6.0+** 中可用。宿主应用可以使用 `DiditSdk.shared.dismiss()` 以编程方式结束正在进行的验证。当宿主需要接管屏幕时（例如应用进入后台），推荐使用这种方式关闭验证。```swift theme={null}
DiditSdk.shared.dismiss()
````dismiss()` 会走 SDK 的常规完成流程：关闭已展示的界面、重置内部状态，并使用 `.cancelled(session:)` 调用 `.diditVerification` 处理程序。如果已创建会话，该结果会携带当前的 `sessionId`。没有进行中的验证时，调用此方法不会产生任何效果。### 应用进入后台时的关闭示例```swift theme={null}
NotificationCenter.default.addObserver(
    forName: UIApplication.didEnterBackgroundNotification,
    object: nil,
    queue: .main
) { _ in
    DiditSdk.shared.dismiss()
}
```> [!WARNING]
>
> 请**勿**设置 `DiditSdk.shared.isPresented = false` 来关闭验证。该标志只在上升沿时触发展示，将其设为 `false` 不会产生任何效果。同样，在最顶层的视图控制器上调用 UIKit 的 `dismiss(animated:)` 也不受支持：它会绕过 SDK 的完成流程，因此你的 `.diditVerification` 处理程序永远不会被触发。***## 观察 SDK 状态你可以观察 SDK 状态，以便自定义加载界面：```swift theme={null}
struct CustomView: View {
    @ObservedObject private var sdk = DiditSdk.shared
    
    var body: some View {
        VStack {
            switch sdk.state {
            case .idle:
                Text("Ready to verify")
            case .creatingSession:
                ProgressView("Creating session...")
            case .loading:
                ProgressView("Loading...")
            case .ready:
                Text("Verification in progress")
            case .error(let message):
                Text("Error: \(message)")
            }
        }
    }
}
```***## 端到端示例，从后端会话到 iOS SDK 再到结果这种模式是可用于生产环境的集成方案。你的后端创建会话，iOS 应用接收 `session_token`，然后由 SDK 运行整个流程。### 1. 在后端创建会话```ts theme={null}
// Node.js / Express example. Run on your server. NEVER ship DIDIT_API_KEY to the device.
import express from "express";

const app = express();
app.use(express.json());

app.post("/api/verification/start", async (req, res) => {
  const response = await fetch("https://verification.didit.me/v3/session/", {
    method: "POST",
    headers: {
      "x-api-key": process.env.DIDIT_API_KEY!,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      workflow_id: process.env.DIDIT_WORKFLOW_ID,
      vendor_data: req.body.userId, // your stable user id
    }),
  });

  if (!response.ok) {
    return res.status(500).json({ error: `Didit ${response.status}` });
  }

  const { session_id, session_token } = await response.json();
  // Persist session_id <-> userId so you can match the webhook later.
  res.json({ session_id, session_token });
});
```### 2. 在 iOS 中交换令牌并启动 SDK```swift theme={null}
import SwiftUI
import DiditSDK

struct VerifyButton: View {
    let userId: String
    @State private var error: String?

    var body: some View {
        Button("Verify Identity") {
            Task { await start() }
        }
        .diditVerification { result in
            switch result {
            case .completed(let session) where session.status == .approved:
                // Optimistically reflect success in the UI; rely on the webhook
                // delivered to your backend as the source of truth.
                break
            case .completed(let session):
                print("Terminal status: \(session.status.rawValue)")
            case .cancelled:
                print("User cancelled")
            case .failed(let err, _):
                error = err.localizedDescription
            }
        }
    }

    private func start() async {
        do {
            var req = URLRequest(url: URL(string: "https://your-backend.example.com/api/verification/start")!)
            req.httpMethod = "POST"
            req.setValue("application/json", forHTTPHeaderField: "Content-Type")
            req.httpBody = try JSONEncoder().encode(["userId": userId])

            let (data, _) = try await URLSession.shared.data(for: req)
            struct Payload: Decodable { let session_token: String }
            let payload = try JSONDecoder().decode(Payload.self, from: data)

            await MainActor.run {
                DiditSdk.shared.startVerification(token: payload.session_token)
            }
        } catch {
            self.error = error.localizedDescription
        }
    }
}
```### 3. 在后端通过 Webhook 接收最终决策SDK 结果便于界面反馈，但权威结果通过 Webhook 送达。HMAC 验证请参阅 [Webhooks 指南](https://docs.didit.me/zh/integration/webhooks.md)。```ts theme={null}
app.post("/api/webhooks/didit", express.raw({ type: "application/json" }), (req, res) => {
  // 1. Verify the X-Signature header (see Webhooks docs)
  // 2. Parse the body
  const event = JSON.parse(req.body.toString());
  if (event.webhook_type === "status.updated") {
    // event.session_id, event.status ("Approved" | "Declined" | "In Review" | ...)
    // Look up the user by session_id and update their verification state.
  }
  res.sendStatus(200);
});
```***## 完整的 SwiftUI 示例```swift theme={null}
import SwiftUI
import DiditSDK

struct HomeView: View {
    @State private var resultMessage: String?
    @State private var isVerified = false
    
    var body: some View {
        VStack(spacing: 24) {
            if isVerified {
                Image(systemName: "checkmark.circle.fill")
                    .font(.system(size: 64))
                    .foregroundColor(.green)
                Text("Identity Verified")
                    .font(.title2)
            } else {
                Button("Verify Identity") {
                    startVerification()
                }
                .font(.headline)
                .padding(.horizontal, 32)
                .padding(.vertical, 16)
                .background(Color.blue)
                .foregroundColor(.white)
                .cornerRadius(12)
            }
            
            if let message = resultMessage {
                Text(message)
                    .foregroundColor(.secondary)
                    .multilineTextAlignment(.center)
                    .padding()
            }
        }
        .diditVerification { result in
            handleResult(result)
        }
    }
    
    private func startVerification() {
        let config = DiditSdk.Configuration(
            languageLocale: .english
        )
        
        // Method 1: UniLink — no backend required
        DiditSdk.shared.startVerification(
            workflowId: "your-workflow-id",
            vendorData: "user-123",
            configuration: config
        )
        
        // Method 2: Backend Session — full parameter control
        // DiditSdk.shared.startVerification(
        //     token: "your-session-token",
        //     configuration: config
        // )
    }
    
    private func handleResult(_ result: VerificationResult) {
        switch result {
        case .completed(let session):
            if session.status == .approved {
                isVerified = true
            }
            resultMessage = """
                Status: \(session.status.rawValue)
                Session: \(session.sessionId)
                Country: \(session.country ?? "N/A")
                Document: \(session.documentType ?? "N/A")
                """
                
        case .cancelled(let session):
            resultMessage = "Cancelled - Session: \(session?.sessionId ?? "unknown")"
            
        case .failed(let error, _):
            resultMessage = "Failed: \(error.localizedDescription)"
        }
    }
}
```***


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.