Skip to main content
A lightweight, server-driven iOS SDK for identity verification with minimal configuration required.

GitHub Repository

View source code and examples on GitHub

Requirements

RequirementMinimum Version
iOS13.0+
Xcode15.0+
Swift5.9+

iOS Version Compatibility

iOS VersionFeatures Available
iOS 13.0 - 14.xAll features except NFC passport reading
iOS 15.0+All features including NFC passport reading

Installation

Add the package to your project using Xcode:
  1. Go to File > Add Package Dependencies
  2. Enter the repository URL:
https://github.com/didit-protocol/sdk-ios
  1. Select the version and click Add Package

CocoaPods

Add the following to your Podfile:
pod 'DiditSDK'
Then run:
pod install
When installing via CocoaPods on apps targeting iOS 13/14, you may see a warning about NFCPassportReader requiring iOS 14+. This warning can be safely ignored — the SDK will work correctly, and NFC functionality is automatically disabled on older iOS versions.

Permissions

The SDK requires the following permissions. Add these to your app’s Info.plist:
PermissionInfo.plist KeyDescriptionRequired
CameraNSCameraUsageDescriptionDocument scanning and face verification✅ Yes
MicrophoneNSMicrophoneUsageDescriptionVideo recording for liveness checks✅ Yes
Photo LibraryNSPhotoLibraryUsageDescriptionUpload documents from device gallery✅ Yes
NFCNFCReaderUsageDescriptionRead NFC chips in passports/ID cards⚠️ If using NFC
LocationNSLocationWhenInUseUsageDescriptionGeolocation for fraud prevention❌ Optional

Example Info.plist Entries

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

To enable NFC reading for passports and ID cards with chips:
  1. Add NFC Capability in Xcode:
    • Select your target → Signing & Capabilities+ CapabilityNear Field Communication Tag Reading
  2. Add ISO7816 Identifiers to Info.plist:
<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
<array>
    <string>D23300000045737445494420763335</string>
    <string>A0000002471001</string>
    <string>A0000002472001</string>
    <string>00000000000000</string>
</array>
  1. Add Entitlements (in your .entitlements file):
<key>com.apple.developer.nfc.readersession.formats</key>
<array>
    <string>TAG</string>
</array>
Simulator Limitation: The iOS SDK requires CoreNFC, which is not fully available on simulators. Since Xcode 12, libnfshared.dylib is missing from simulators. Test NFC features on physical devices only.

App Store Review: Even if you disable NFC in your workflow, Apple may request a demo video because NFC code is part of the SDK binary. You can download our NFC demo video to submit to Apple: Download NFC Demo Video


Quick Start

SwiftUI Integration

import SwiftUI
import DiditSDK

struct ContentView: View {
    var body: some View {
        Button("Verify Identity") {
            // Option A: With session token from your backend
            DiditSdk.shared.startVerification(token: "your-session-token")
            
            // Option B: With workflow ID (SDK creates session)
            // DiditSdk.shared.startVerification(workflowId: "your-workflow-id")
        }
        .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 Integration

import UIKit
import DiditSDK

class VerificationViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Start verification when ready
        DiditSdk.shared.startVerification(token: "your-session-token")
    }
}

Integration Methods

The SDK supports two integration methods: Create a session on your backend using the Create Verification Session API, then pass the token to the SDK:
// 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)
This approach gives you full control over:
  • Associating sessions with your users (vendor_data)
  • Setting custom metadata
  • Configuring callbacks per session

Method 2: Workflow ID (Simpler Integration)

For simpler integrations, the SDK can create sessions directly using your workflow ID:
DiditSdk.shared.startVerification(
    workflowId: "your-workflow-id",
    vendorData: "user-123",
    contactDetails: ContactDetails(email: "[email protected]"),
    expectedDetails: ExpectedDetails(firstName: "John", lastName: "Doe")
)

Configuration

Customize the SDK behavior:
let config = DiditSdk.Configuration(
    fontFamily: "Avenir-Medium",    // Custom font (must be registered in your app)
    loggingEnabled: false,          // Enable debug logging
    languageLocale: .english        // Force specific language
)

DiditSdk.shared.startVerification(
    token: "your-session-token",
    configuration: config
)

Configuration Options

PropertyTypeDefaultDescription
fontFamilyString?System fontCustom font family name (must be registered in your app)
loggingEnabledBoolfalseEnable SDK debug logging
languageLocaleSupportedLanguage?Device localeForce specific language
Theming & Colors: Colors, backgrounds, and intro screen settings are configured through your White Label settings in the Didit Console, not in the SDK configuration. This ensures consistent branding across all platforms.

Language Support

The SDK supports 40+ languages. If no language is specified, the SDK uses the device locale with English as fallback.
// Use device locale (default)
let config = DiditSdk.Configuration()

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

// Detect device locale programmatically
let deviceLanguage = SupportedLanguage.fromDeviceLocale()
View All Supported Languages →

Advanced Options

Contact Details (Prefill & Notifications)

Provide contact details to prefill verification forms and enable email notifications:
let contactDetails = ContactDetails(
    email: "[email protected]",
    sendNotificationEmails: true,  // Send status update emails
    emailLang: "en",               // Email language (ISO 639-1)
    phone: "+14155552671"          // E.164 format
)

DiditSdk.shared.startVerification(
    workflowId: "your-workflow-id",
    contactDetails: contactDetails
)

Expected Details (Cross-Validation)

Provide expected user details for automatic cross-validation with extracted document data:
let expectedDetails = ExpectedDetails(
    firstName: "John",
    lastName: "Doe",
    dateOfBirth: "1990-05-15",     // YYYY-MM-DD format
    nationality: "USA",            // ISO 3166-1 alpha-3
    country: "USA"
)

DiditSdk.shared.startVerification(
    workflowId: "your-workflow-id",
    expectedDetails: expectedDetails
)

Custom Metadata

Store custom JSON metadata with the session (not displayed to user):
DiditSdk.shared.startVerification(
    workflowId: "your-workflow-id",
    vendorData: "user-123",
    metadata: "{\"internalId\": \"abc123\", \"source\": \"mobile-app\"}"
)

Handling Results

The VerificationResult enum provides the outcome of the verification:

Result Cases

CaseDescription
.completed(session:)Verification flow completed (check session.status for result)
.cancelled(session:)User cancelled the verification flow
.failed(error:session:)An error occurred during verification

SessionData Properties

PropertyTypeDescription
sessionIdStringUnique session identifier
statusVerificationStatus.approved, .pending, or .declined
countryString?Country code (ISO 3166-1 alpha-3)
documentTypeString?Document type used for verification

Error Types

ErrorDescription
.sessionExpiredThe session has expired
.networkErrorNetwork connectivity issue
.cameraAccessDeniedCamera permission not granted
.unknown(String)Other error with message

Complete Result Handling Example

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

Observing SDK State

You can observe the SDK state for custom loading UI:
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)")
            }
        }
    }
}

Complete SwiftUI Example

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
        )
        
        // Option A: With session token from your backend
        DiditSdk.shared.startVerification(
            token: "your-session-token",
            configuration: config
        )
        
        // Option B: With workflow ID (SDK creates session)
        // DiditSdk.shared.startVerification(
        //     workflowId: "your-workflow-id",
        //     vendorData: "user-123",
        //     contactDetails: ContactDetails(email: "[email protected]"),
        //     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)"
        }
    }
}