DLens SDK Documentation

DLens is a production-grade iOS SDK for scanning driver licences, passports, and national ID cards — entirely on-device, with no data stored or transmitted.

v1.0.0 · Stable iOS 17.0+ Swift 5.9+ Xcode 15+

Overview

DLens uses the iOS Vision framework and AVFoundation to detect and parse identity documents directly on device. No network connection is required. No document data leaves the device at any point.

The SDK supports three scanning pipelines:

Requirements

Installation

XCFramework (Recommended)

1

Download the framework

Request your trial key at dlens.io/#contact — we'll send you DLensSDK.xcframework along with your key.

2

Add to your project

Drag DLensSDK.xcframework into your Xcode project navigator. In the dialog, check Copy items if needed.

3

Embed & Sign

In your target's General → Frameworks, Libraries, and Embedded Content, set DLensSDK.xcframework to Embed & Sign.

4

Add camera permission

Add NSCameraUsageDescription to your Info.plist with a description explaining why your app needs camera access.

💡 Tip: The framework must be Embed & Sign, not just Do Not Embed. Without embedding, the framework won't be copied into your app bundle and the install will fail with a missing signature error.

Quick Start

Three steps to your first scan:

DLensSampleApp.swift
import SwiftUI
import DLensSDK

// Step 1: Initialise at app launch
@main
struct MyApp: App {
    init() {
        try? DLensKit.initialize(
            apiKey: "YOUR_API_KEY",
            secret: "YOUR_SECRET"
        )
    }
    var body: some Scene {
        WindowGroup { ContentView() }
    }
}

// Step 2: Present the scanner
struct ContentView: View {
    @State private var isScanning = false

    var body: some View {
        Button("Scan Licence") { isScanning = true }
        .fullScreenCover(isPresented: $isScanning) {

            // Step 3: Handle the result
            DLensScanner { result in
                print(result.fullName)
                print(result.licenseNumber)
                isScanning = false
            }
        }
    }
}

Initialisation

Call DLensKit.initialize() once at app launch — typically in your App.init() or AppDelegate.application(_:didFinishLaunchingWithOptions:). Calling it more than once is safe and is a no-op after the first successful call.

Initialisation
import DLensSDK

do {
    try DLensKit.initialize(
        apiKey: "YOUR_API_KEY",
        secret: "YOUR_SECRET",
        configuration: {
            var c = DLensConfiguration()
            c.showTorchButton   = true
            c.showZoomControls  = true
            c.instructionText   = "Aim at the barcode on the back"
            return c
        }()
    )
} catch {
    // DLensError.invalidKey — key is malformed or expired
    // DLensError.bundleMismatch — key locked to a different bundle ID
    print("DLens init failed: \(error.localizedDescription)")
}

SwiftUI Integration

Use DLensScanner inside a .fullScreenCover or .sheet. It fills the presented area with the camera UI automatically.

SwiftUI
DLensScanner(
    onResult: { result in
        // Called on main thread when scan succeeds
        self.scanResult = result
        self.isScanning  = false
    },
    onError: { error in
        // Camera permission denied, key expired, etc.
        print(error.localizedDescription)
        self.isScanning = false
    },
    onCancel: {
        // User tapped the cancel/back button
        self.isScanning = false
    }
)

UIKit Integration

UIKit
let scanner = DLensScannerViewController(
    onResult: { [weak self] result in
        self?.handleResult(result)
    },
    onError: { error in
        print(error)
    }
)
present(scanner, animated: true)

Configuration

PropertyTypeDefaultDescription
showTorchButtonBooltrueShow the torch toggle button in the camera UI
showZoomControlsBooltrueShow manual +/− zoom buttons
instructionTextString"Aim at barcode…"Instruction label shown below the viewfinder
timeoutSecondsTimeInterval60Seconds before the scanner auto-dismisses without a result
accentColorColor.blueTint colour for UI elements

DLensScanResult

All fields are optional String? or Date? unless marked otherwise.

FieldTypeDescription
firstNameStringGiven name — always present
lastNameStringSurname — always present
middleNameString?Middle name (AAMVA only)
fullNameStringComputed: firstName + lastName
dateOfBirthDate?Parsed date of birth
formattedDateOfBirthString?DOB formatted as "MMM d, yyyy"
licenseNumberStringDocument/licence number
licenseClassString?Vehicle class codes (AAMVA)
restrictionsString?Driving restrictions (AAMVA)
endorsementsString?Driving endorsements (AAMVA)
expiryDateDate?Document expiry date
issueDateDate?Document issue date
streetAddressString?Street address (AAMVA)
cityString?City (AAMVA)
stateString?State/Province code (AAMVA)
postalCodeString?Postal/ZIP code (AAMVA)
countryString?Country code
genderString?"M", "F", or "X"
eyeColorString?Eye colour code (AAMVA)
heightString?Height string (AAMVA)
rawPayloadStringRaw barcode/MRZ string

Errors

Error CaseDescription
DLensError.notInitializedDLensKit.initialize() was not called before presenting the scanner
DLensError.invalidKeyThe API key is malformed, expired, or has been revoked
DLensError.bundleMismatchThe key was issued for a different app bundle ID
DLensError.cameraPermissionDeniedThe user denied camera access. Direct them to Settings.

Licence Keys

DLens uses HMAC-SHA256 signed JSON tokens. Each key encodes your bundle ID, expiry date, and a signature that can be verified entirely on-device without a network call.

⚠️ Keep your secret private. Never commit your API key or secret to a public repository. For production apps, load credentials from your app's secure configuration — not hardcoded in source.

For development, you can generate test credentials that work without a paid licence:

Development Keys
// Generate dev credentials (for testing only)
let (apiKey, secret) = DLensKit.makeTestCredentials()
try DLensKit.initialize(apiKey: apiKey, secret: secret)

Driver Licences

DLens parses AAMVA-standard PDF417 barcodes from all 50 US states, Washington DC, and all Canadian provinces and territories. Both legacy AAMVA 2000 and modern ANSI D20 formats are supported with automatic version detection.

Scan the back of the licence where the PDF417 barcode is located. The scanner automatically activates hardware barcode detection via AVCaptureMetadataOutput for maximum speed.

Passports

DLens reads the Machine-Readable Zone (MRZ) on the photo data page of ICAO TD3-format passports from 190+ countries. Full ICAO 9303 check digit validation is performed on all fields before returning a result.

Scan the bottom two lines of the photo page (the MRZ zone). The scanner uses Vision framework OCR with a two-strategy parser to handle real-world imperfections.

National ID Cards

TD1 (three-line, 30 chars) and TD2 (two-line, 36 chars) MRZ formats are supported, covering EU national ID cards, Middle Eastern national IDs, biometric residence permits, and many other document types.

Developer FAQ

The scanner is not finding my barcode. What should I try?

Ensure good lighting — the auto-zoom sweep will step from 1× to 2.5× automatically. Make sure the barcode is flat and not folded. Very worn or damaged barcodes may fall back to Vision software decoding which takes slightly longer.

Can I customise the scanner UI appearance?

Yes — use DLensConfiguration.accentColor to change the tint colour, and instructionText to customise the prompt. Full UI theming is available on Enterprise plans.

How do I handle camera permission denial?

Catch DLensError.cameraPermissionDenied in your onError handler and redirect the user to UIApplication.openSettingsURLString.

Does the SDK work offline?

Yes. All scanning is on-device and requires no internet connection. Licence validation is cached locally on first use and re-validated periodically when a connection is available.