Appearance
Usage Guide
Quick Start
swift
import KinegramEmrtdConnector
// Unique transaction ID, usually from your server
let validationId = UUID().uuidString
// Initialize connector
let connector = EmrtdConnector(
serverURL: URL(string: "wss://docval.kurzdigital.com/ws2/validate")!,
validationId: validationId,
clientId: "YOUR-CLIENT-ID"
)
// Validate with MRZ
let mrzKey = MRZKey(
documentNumber: "P1234567",
birthDateyyMMdd: "900101",
expiryDateyyMMdd: "250101"
)
let result = try await connector.validate(with: mrzKey)
if result.isValid {
print("Document holder: \(result.mrzInfo?.primaryIdentifier ?? "")")
}The SDK handles connection, validation, and disconnection automatically.
Advanced Usage (Manual Connection)
If you need more control, you can use the explicit connection approach:
swift
// Connect first
try await connector.connect()
// Then validate
let result = try await connector.startValidation(accessKey: mrzKey)
// Disconnect when done
await connector.disconnect()Using With CAN
For documents that support PACE with a Card Access Number (a 6-digit number printed on the document):
swift
let canKey = CANKey(can: "123456") // 6-digit CAN
let result = try await connector.validate(with: canKey)INFO
CAN authentication only works with documents that support PACE (Password Authenticated Connection Establishment). It cannot be used with BAC (Basic Access Control).
Reading PACE-Enabled Documents
Some identity documents require PACE (Password Authenticated Connection Establishment) polling to be detected. This includes French ID cards (FRA ID) and Omani ID cards (OMN ID).
swift
// Enable PACE polling for PACE-enabled documents (requires iOS 16+)
let canKey = CANKey(can: "123456")
let result = try await connector.validate(with: canKey, usePACEPolling: true)Important:
- PACE polling is only available on iOS 16 and later
- PACE polling cannot detect standard passports, use it only when you know the document requires it
- A
PACEPollingNotAvailableerror will be thrown if you try to use PACE polling on iOS 15 or earlier
Automatic PACE Selection (by Document Info)
If you don't want to decide usePACEPolling yourself, you can provide the document type and issuing country and let the SDK decide. This currently enables PACE polling for known ID cards that require it (e.g., FRA ID, OMN ID) and keeps it disabled for standard passports.
swift
// Auto-select PACE polling based on document info
let canKey = CANKey(can: "123456")
// Option A: Specify document type explicitly
let result = try await connector.validate(
with: canKey,
documentType: .idCard,
issuingCountry: "FRA" // ISO 3166-1 alpha-3
)
// Option B: Derive document type from MRZ document code prefix
let docType = DocumentKind.fromMRZDocumentCode("ID")
let result2 = try await connector.validate(
with: canKey,
documentType: docType,
issuingCountry: "FRA"
)Notes:
- You still need the PACE entitlement in your app when PACE might be used (see Installation).
- For unknown countries or for passports, the SDK defaults to no PACE polling (you can still use the manual flag if needed).
Custom HTTP Headers
If your server requires custom headers (e.g., for authentication), you can provide them in the EmrtdConnector initializer. These headers are sent in the WebSocket handshake and forwarded by the DocVal Server to your result server:
swift
let headers = [
"Authorization": "Bearer your-token",
"X-Custom-Header": "value"
]
let connector = EmrtdConnector(
serverURL: URL(string: "wss://docval.kurzdigital.com/ws2/validate")!,
validationId: UUID().uuidString,
clientId: "YOUR-CLIENT-ID",
httpHeaders: headers
)Fire-and-Forget Mode
If you don't need to receive the validation result back from the server, you can use the receiveResult parameter:
swift
let connector = EmrtdConnector(
serverURL: URL(string: "wss://docval.kurzdigital.com/ws2/validate")!,
validationId: UUID().uuidString,
clientId: "YOUR-CLIENT-ID",
receiveResult: false
)
// The validate call will complete after sending data to the server
try await connector.validate(with: mrzKey)When receiveResult is false, the server won't send back the validation result, reducing latency and bandwidth usage.
Diagnostics
Enable diagnostic data collection on the DocVal Server to help troubleshoot eMRTD reading problems:
swift
let connector = EmrtdConnector(
serverURL: URL(string: "wss://docval.kurzdigital.com/ws2/validate")!,
validationId: UUID().uuidString,
clientId: "YOUR-CLIENT-ID",
enableDiagnostics: true
)WARNING
When diagnostics are enabled, the DocVal Server collects additional debugging information that may contain personal data. Make sure to obtain user consent if required.
See Diagnostic Session for more information.
Error Handling
The SDK uses Swift's native error handling. Errors are thrown from the validate call and can be caught by type:
swift
do {
let result = try await connector.validate(with: mrzKey)
} catch EmrtdConnectorError.nfcNotAvailable(let reason) {
print("NFC not available: \(reason)")
} catch EmrtdConnectorError.connectionTimeout {
print("Timeout - hold passport steady")
} catch EmrtdConnectorError.incompleteRead(let missingFiles, let reason) {
print("Missing files: \(missingFiles.joined(separator: ", "))")
print("Reason: \(reason)")
} catch EmrtdReaderError.accessControlFailed {
print("Wrong MRZ/CAN")
} catch {
print("Error: \(error)")
}Progress Updates
swift
// Set delegate for progress updates
connector.delegate = self
// Implement delegate method
func connector(_ connector: EmrtdConnector, didUpdateNFCStatus status: NFCProgressStatus) async {
print(status.alertMessage)
// Shows: "Reading Document Data\n▮▮▮▮▯▯▯"
}Session Close Callback
The SDK provides a callback that fires when the WebSocket session closes, regardless of success or failure. This is the recommended way to handle session completion:
swift
func connector(_ connector: EmrtdConnector, didClose info: CloseInfo) async {
if info.postConfirmed {
// Server confirmed successful post to result server (Close Code 1000)
print("Results posted successfully")
} else {
// Either an error occurred or connection was lost
// Check didFailWithError for error details, or verify with your backend
print("Post not confirmed")
}
}CloseInfo Properties
| Property | Type | Description |
|---|---|---|
postConfirmed | Bool | true if server confirmed successful post (code 1000) |
trigger | CloseTrigger | What caused the session to close |
code | Int? | WebSocket close code (1000 = success) |
reason | String? | Close reason from server |
CloseTrigger Values
| Trigger | Description |
|---|---|
.server | Server sent explicit CLOSE message |
.connectionLost | Connection dropped unexpectedly |
.timeout | Timeout waiting for server CLOSE |
.client | SDK called disconnect() |
Migration from connectorDidSuccessfullyPostToServer
The old connectorDidSuccessfullyPostToServer callback is deprecated. It only fires on success (Code 1000), leaving you without information on failures or connection drops.
Before (deprecated):
swift
func connectorDidSuccessfullyPostToServer(_ connector: EmrtdConnector) async {
print("Posted successfully")
}After (recommended):
swift
func connector(_ connector: EmrtdConnector, didClose info: CloseInfo) async {
if info.postConfirmed {
print("Posted successfully")
}
}Localization
The SDK provides English status messages by default. To localize the NFC dialog messages:
swift
// Configure localization before validation
connector.nfcStatusLocalization = { status in
switch status.step {
case .waitingForPassport:
return NSLocalizedString("nfc.waitingForPassport", comment: "")
case .readingDG1:
return NSLocalizedString("nfc.readingDocumentData", comment: "")
default:
return status.alertMessage // Fall back to English
}
}
// The NFC dialog will now show your localized messages
let result = try await connector.validate(with: accessKey)Debug Logging
Debug logging is automatically enabled in DEBUG builds. Log messages will appear in the console during development.