> ## 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: 모바일 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 이상이 필요합니다.** Illinois, Texas 또는 Colorado 주법이 적용되는 사용자의 경우, Didit은 첫 얼굴 촬영 전에 [명시적인 생체 정보 동의](https://docs.didit.me/ko/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 여권/eID 칩 읽기 기능을 더한 변형입니다.
- **All**: 모든 기능을 포함합니다. 서브스펙이나 제품 이름을 지정하지 않고 `DiditSDK`를 설치하면 이 변형이 기본값으로 선택됩니다.각 변형이 앱 번들에 추가하는 용량(arm64 Mach-O, `4.9.0` 기준 측정)은 다음과 같습니다. **Core** 약 11.1 MB, **NFC** 약 11.7 MB와 OpenSSL 약 4 MB, **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` 번들 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는 iOS 13 또는 14 대상에 `DiditSDK`나 `DiditSDKNFC`를 추가하는 것을 경고 없이 허용합니다. 하지만 두 제품 모두 `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` 후에는 `.xcodeproj`가 아니라 생성된 `.xcworkspace`를 여세요.> [!NOTE]
>
> **Xcode 15+ rsync 오류**: 빌드 중 `Operation not permitted` rsync 오류가 나타나면, 프로젝트의 **Build Settings → User Script Sandboxing**(`ENABLE_USER_SCRIPT_SANDBOXING`)을 Debug와 Release 모두에서 **아니요**로 설정하세요.### 수동 설치 (XCFramework)각 릴리스는 변형마다 하나의 XCFramework를 배포합니다. 변형에 맞는 zip 파일을 [GitHub 릴리스](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` |***## 권한SDK에는 다음 권한이 필요합니다. 앱의 `Info.plist`에 다음 항목을 추가하세요.| 권한 | Info.plist 키 | 설명 | 필수 여부 |
| - | - | - | - |
| 카메라 | `NSCameraUsageDescription` | 문서 스캔 및 얼굴 인증 | ✅ 예 |
| 마이크 | `NSMicrophoneUsageDescription` | 라이브니스 검사를 위한 영상 녹화 | ✅ 예 |
| 사진 보관함 | `NSPhotoLibraryUsageDescription` | 기기 갤러리에서 문서 업로드 | ✅ 예 |
| NFC | `NFCReaderUsageDescription` | 여권 및 신분증의 NFC 칩 읽기 | ⚠️ 전체 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/ko/core-technology/nfc-verification/overview.md#일본-마이넘버카드-jpki)를 통해 일본 마이넘버 카드를 읽을 수 있습니다. 이 식별자가 없으면 iOS는 해당 카드를 SDK에 보고하지 않습니다. 마이넘버 카드 읽기는 아직 프로덕션에서 활성화되어 있지 않으며, 이 식별자는 다른 문서에는 영향을 주지 않으므로 지금 추가해도 됩니다.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 관련 코드가 SDK 바이너리에 포함되므로, 워크플로에서 NFC를 사용하지 않더라도 Apple이 심사 중 데모 영상을 요청할 수 있습니다. Apple에 제출할 NFC 데모 영상은 [support@didit.me](mailto:support@didit.me)로 요청하세요. 이 내용은 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는 두 가지 연동 방법을 지원합니다.### 방법 1: UniLink (가장 간단함)백엔드가 필요하지 않습니다. SDK는 [Didit 콘솔](https://business.didit.me)에서 가져온 워크플로 ID를 사용하여 세션을 직접 생성합니다. UniLink 방법(`startVerification(workflowId:)`)은 `vendorData`만 지원합니다. 다른 세션 파라미터가 필요하면 방법 2를 사용하세요.> [!NOTE]
>
> UniLink를 통해 세션에 `vendorData`를 연결하려면 [Didit 콘솔](https://business.didit.me)에서 **벤더 데이터** 옵션을 활성화하세요.```swift theme={null}
DiditSdk.shared.startVerification(
    workflowId: "your-workflow-id",
    vendorData: "user-123"
)
```### 방법 2: 백엔드 세션 (프로덕션 권장)백엔드가 [인증 세션 생성 API](https://docs.didit.me/ko/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/ko/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` | 완료되면 인증 UI를 자동으로 닫습니다(Web SDK 대응 옵션: `closeModalOnComplete`) |
| `defaultDocumentCamera` | `CameraLens` | `.back` | 문서 촬영 화면이 열릴 때 사용하는 렌즈 |
| `defaultLivenessCamera` | `CameraLens` | `.front` | 얼굴(라이브니스) 촬영 화면이 열릴 때 사용하는 렌즈 |
| `showDocumentCameraSwitchButton` | `Bool` | `true` | 문서 촬영 화면에 촬영 중 카메라 전환 버튼을 표시합니다 |
| `showLivenessCameraSwitchButton` | `Bool` | `true` | 얼굴 촬영 화면에 촬영 중 카메라 전환 버튼을 표시합니다 |### 카메라 옵션카메라 옵션은 [Web SDK 카메라 옵션](https://docs.didit.me/ko/integration/web-sdks/javascript-sdk.md#카메라-옵션)과 일치합니다.
각 렌즈는 `.front` 또는 `.back`입니다.
기기에 요청한 렌즈가 없으면 SDK는 사용 가능한 첫 번째 카메라를 사용합니다.
카메라가 하나뿐인 기기에서는 카메라 전환 버튼도 표시되지 않습니다.
전환 옵션을 `false`로 설정하면 사용자를 해당 단계의 기본 렌즈에 고정할 수 있습니다.```swift theme={null}
let configuration = DiditSdk.Configuration(
    defaultLivenessCamera: .back,
    showLivenessCameraSwitchButton: false
)
```> [!NOTE]
>
> **테마 및 색상**: 색상, 배경, 인트로 화면 설정은 SDK 구성이 아니라 Didit Console의 [화이트 라벨 설정](https://business.didit.me)에서 구성합니다. 이렇게 하면 모든 플랫폼에서 브랜딩이 일관되게 유지됩니다.> [!NOTE]
>
> `showLanguageSelector`, `showCloseButton`, `showExitConfirmation`, `closeOnComplete` 옵션은 Web SDK의 `DiditSdkConfiguration`과 일치합니다. 모바일 전용 옵션인 `languageLocale`과 `fontFamily`는 모바일 SDK가 전체 인증 UI를 네이티브로 렌더링하기 때문에 존재합니다(iframe 안에서 호스팅 프론트엔드에 렌더링을 맡기는 Web SDK와 다릅니다).***## 언어 지원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/ko/integration/supported-languages.md)*****## 고급 세션 파라미터고급 세션 파라미터(`contact_details`, `expected_details`, `metadata`, `callback`)를 사용하려면 **Backend Session** 방식을 사용하세요. 백엔드가 전체 파라미터와 함께 [Create Verification Session API](https://docs.didit.me/ko/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에 다시 연결할 수 있게 되는 즉시 화면을 스스로 닫고 사용자는 있던 단계부터 이어서 진행합니다.
이 화면에는 **다시 시도** 버튼이 있고, `showCloseButton`이 `true`(기본값)이면 **인증 종료** 버튼도 표시됩니다.
종료하면 인증이 `.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의 일반 완료 파이프라인을 거칩니다. 표시 중인 UI를 닫고, 내부 상태를 초기화하며, 세션이 생성되었다면 현재 `sessionId`를 담은 `.cancelled(session:)`으로 `.diditVerification` 핸들러를 호출합니다. 활성 인증이 없으면 아무 동작도 하지 않습니다.### 예제: 앱이 백그라운드로 전환될 때 종료하기```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 상태 관찰하기커스텀 로딩 UI를 구현하기 위해 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 결과는 UI 피드백에 편리하지만, 최종 결과는 webhook으로 전달됩니다. HMAC 검증 방법은 [Webhooks 가이드](https://docs.didit.me/ko/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.