The Truora Validations SDK allows you to integrate identity verification features directly into your native mobile applications. The SDK handles the complexity of capturing the user’s identity documents, facial recognition, and liveness detection to verify their identity against backend records.
Requirements
Platform Requirements
iOS: 13.0 or higher
Permissions Required
Camera access (for document and face capture)
Internet access (for API communication)
Prerequisites & Authentication
The Key Provider Interface
To prevent hardcoding sensitive logic, you must implement the TruoraAPIKeyGetter interface in your code. This allows you to retrieve the API key from a secure location (like a compiled secret, a secure storage enclave, or an obfuscated string). This interface is how the sdk will get access to the api key for the validations, the api key must be of type sdk (temporary), any other key type will result in an error.
import Foundation
import TruoraValidationsSDK
/// A standalone implementation of TruoraAPIKeyGetter in Swift.
/// This handles the asynchronous retrieval of the API key for the Validations SDK.
public class SecureApiKeyProvider: TruoraAPIKeyGetter {
private let apiKey: String
public init(apiKey: String) {
self.apiKey = apiKey
}
/**
* Retrieves the API key.
* Marked as 'async throws' to support secure network-based or
* disk-encrypted (Keychain) lookups that might fail or take time.
*/
public func getApiKeyFromSecureStorage() async throws -> String {
// In a production app, you might fetch this from the iOS Keychain
// using a library like SwiftKeychainWrapper or LocalAuthentication.
return apiKey
}
}
Installation
Add the Truora SDK via Swift Package Manager (SPM) or CocoaPods. In either case you must add the Camera usage description to your Info.plist file, or the app will crash upon launch.
import UIKit
import TruoraValidationsSDK
class ViewController: UIViewController, TruoraAPIKeyGetter {
// 1. Implement the Key Getter
func getApiKeyFromSecureStorage() async throws -> String {
// In a production app, you might fetch this from the iOS Keychain
// using a library like SwiftKeychainWrapper or LocalAuthentication.
}
func startValidation() {
Task {
// 2. Build the SDK
let validation = TruoraValidationsSDK.Builder(
apiKeyGenerator: self,
userId: "user_unique_id"
)
.withValidation { (face: Face) in
// Configure Face specific settings
face
.useAutocapture(true)
.setSimilarityThreshold(0.95)
.setTimeout(60)
.waitForResults(false)
}
.build() // Returns a TruoraValidation<Face> object
// 3. Start the flow
await validation.start(from: self) { [weak self] result in
self?.handleResult(result)
}
}
}
func handleResult(_ result: TruoraValidationResult<ValidationResult>) {
switch result {
case .completed(let validationResult):
print("Success! ID: \(validationResult.validationId)")
print("Status: \(validationResult.status)")
case .error(let error):
print("Validation failed: \(error.localizedDescription)")
// You can check for specific errors like user cancellation
}
}
}
Configuration Options
Before implementing validations, let’s understand the available configuration options.
Using custom validation config and UI config is optional when building the validation:
If no uiConfig is provided then the validation view will show the standard Truora branding, logo and colors
If no validationConfig is provided then default values will be applied like:
No waiting for validation results before finishing the validation process for the user
autocapture of face/doc when it’s detected will be on
More indepth information on the validation configuration options, such as similarity threshold, can be found in the official validations api documentation
The userId parameter is crucial for initializing the Truora SDK. It provides a unique identifier for the user undergoing the validation process.
Purpose: The userId allows Truora to associate validation attempts and reference data (like previously captured reference faces) with a specific user in your system. It is mandatory for linking the validation results to your user base.
Best Practice: This ID should be an immutable, non-sensitive identifier from your database (e.g., a UUID or internal user ID) that uniquely identifies the person. Do not use sensitive PII like an email address or national ID number as the userId.
Language
The language parameter allows you to define the localization of the text and instructions displayed within the validation view.
Customization: By setting the language, you ensure a user-friendly experience by presenting instructions and feedback in the user’s preferred language.
Available Options: The SDK supports several languages. You should provide the language code as a two-letter string (e.g., “es” for Spanish, “en” for English, “pt” for Portuguese). If no language is provided, the SDK will typically default to English or attempt to use the device’s system language, though providing an explicit language is recommended.
Face configuration
To create a face validation you just need to add it when building the validation object withValidation, you can just return the validation config inside the lambda function if you do not want to use any custom configuration
To specify that the validation type you want to use is Face, then explicitly type the lambda function as accepting a Face config validation type
// 1. Build the SDK
let validation = TruoraValidationsSDK.Builder(
apiKeyGenerator: self,
userId: "user_unique_id"
)
.withValidation { (face: Face) in
// Configure Face specific settings
face
.useAutocapture(true)
.setSimilarityThreshold(0.95)
.setTimeout(60)
.waitForResults(false)
}
.build() // Returns a TruoraValidation<Face> object
Configuration Parameters:
Parameter
Type
Default
Description
useAutocapture
bool
true
Automatically capture when face is properly detected
similarityThreshold
double
0.8
Minimum similarity score required (0.0 - 1.0)
timeout
int
60
Maximum time in seconds for the validation
waitForResults
bool
false
Wait for API response before closing the view
referenceFace
ReferenceFace
null
Optional reference image for comparison
Document configuration
To create a face validation you just need to added it when building the validation object withValidation, you can just return the validation config inside the lambda function if you do not want to use any custom configuration
To specify that the validation type you want to use is Document, then explicitly type the lambda function as accepting a Document config validation type
// 1. Build the SDK
let validation = TruoraValidationsSDK.Builder(
apiKeyGenerator: self,
userId: "user_unique_id"
)
.withValidation { (doc: Document) in
doc.useAutocapture(true)
.setSimilarityThreshold(0.95)
.setTimeout(60)
.waitForResults(false)
.setCountry(.co) // Country code (e.g., co, mx, pe)
.setDocumentType(.national_id) // Type of document
}
.build() // Returns a TruoraValidation<Document> object
Configuration Parameters:
Parameter
Type
Default
Description
country
Country
null
Country code for document validation (if not set, user selects)
documentType
DocumentType
null
Type of document (if not set, user selects)
useAutocapture
bool
true
Automatically capture when document is detected. Not supported for passport — the SDK will raise an error if autocapture is enabled with .passport document type
public struct ValidationResult: Codable, Equatable {
public let validationId: String // Unique ID for this validation
public let status: ValidationStatus // Status of the validation
public let confidence: Double?
/// Full validation detail from the API response.
/// Available when the result comes from polling (not for canceled/pending results).
public let detail: ValidationDetail?
}
Validation Statuses
Status
Description
When it occurs
ValidationStatus.success
Validation passed
User successfully passed the validation
ValidationStatus.failure
Validation failed
User did not pass the validation criteria
ValidationStatus.pending
Awaiting processing
Validation is being process
TruoraException Types
When the SDK returns failed, the error can be one of three types:
Exception Type
Description
TruoraException.sdk
Internal SDK error (configuration, permissions, user actions)
func handleResult(result: ValidationResult) {
switch result {
case .completed(let validation):
print('Validation ID: \(validation.validationId)');
print('Status: \(validation.status.rawValue)');
if (validation.status ==.success) {
// User passed the validation
navigateToSuccessScreen();
} else if (validation.status == ValidationStatus.failure) {
// User did not pass the validation criteria
showRetryDialog();
}
break;
case .error(let err):
// SDK error occurred - handle based on error type
handleSdkError(err)
break
case .canceled(let partialResult):
// User cancelled - just go back
goToPrevScreen()
break
}
}
private func handleSDKError(_ error: TruoraException) {
switch error {
case .sdk(let sdkError):
switch sdkError.type {
case .cameraPermissionError:
// Camera permission denied — prompt user to grant it
showAlert(
title: "Camera Permission Required",
message: "Please enable camera access in Settings to continue."
)
default:
showAlert(
title: "SDK Error (\(sdkError.code))",
message: sdkError.errorDescription ?? "Unknown SDK error"
)
}
case .network(let message, _):
showAlert(
title: "Connection Error",
message: "Connection error: \(message)"
)
case .validationApi(let apiError):
showAlert(
title: "API Error",
message: "API error: \(apiError.errorDescription ?? "Unknown API error")"
)
}
}
Implementation
Now that you understand configurations and result handling, let’s implement validations.
Basic Face Validation
Here’s a complete example of implementing face validation in SwiftUI:
func startDocumentValidation() {
isValidating = true
validationStatus = "Starting document validation..."
let validation = TruoraValidationsSDK.Builder(
apiKeyGenerator: self,
userId: "ios-test-user"
)
.withConfig { config in
config
.setPrimaryColor("#435AE0")
.setSurfaceColor("#FFFFFF")
.setErrorColor("#FF5454")
.setLogo("https://your-cdn.com/logo.png", width: 120, height: 40)
}
.withValidation { (face: Face) in
face
.useAutocapture(true)
.waitForResults(true)
return face
}
.build()
Task { @MainActor in
defer { isValidating = false }
guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let rootViewController = windowScene.windows.first?.rootViewController else {
validationStatus = "Could not find root view controller"
return
}
await validation.start(from: rootViewController) { [weak self] result in
self?.handleValidationResult(result)
}
}
}
Troubleshooting
Common Issues
Build fails on iOS
Solution: Run cd ios && pod install and ensure your iOS deployment target is set to 13.0 or higher in both Podfile and Xcode project settings.
“API Key Invalid” error
Solution: Verify your API key is correct and has the proper grants (generator or sdk type). Check that it hasn’t expired.
Validation timeout
Solution: Increase the timeout value in the config, or ensure the device has a stable internet connection.