
Verilive iOS SDK is a library that allows you to integrate liveness verification into your iOS application. It provides a simple UIViewController-based API with a delegate pattern for receiving results.
Deployment Target: iOS 13+
Permissions: Camera (NSCameraUsageDescription in Info.plist)
Orientation: Portrait only (enforced by SDK)
Create a session in your backend by making a POST request to /liveness2/session endpoint.
curl -X POST https://services.<region>.verigram.kz/liveness2/session \
-H 'Content-Type: application/json' \
-H 'X-Verigram-Api-Version: 2.0' \
-H 'X-Verigram-Api-Key: your-test-api-key' \
-d '{ "person_id": "end-user-id" }'
The endpoint returns a JSON object containing session_id, access_token, person_id, and flow_id. Pass these values directly to your iOS app.
Add camera usage description to your app's Info.plist:
<key>NSCameraUsageDescription</key>
<string>Camera access is required for liveness verification</string>
Create and present Verilive2ViewController:
import Verilive2
let veriliveVC = Verilive2ViewController()
veriliveVC.params = VeriliveParams(
personId: "your-person-id",
accessToken: "your-access-token",
flowId: "your-flow-id",
sessionId: "your-session-id"
)
veriliveVC.delegate = self
veriliveVC.modalPresentationStyle = .fullScreen
veriliveVC.isModalInPresentation = true
present(veriliveVC, animated: false)
Implement the delegate to receive results:
extension YourViewController: Verilive2ViewControllerDelegate {
func verilivePassed(result: VeriliveResult) {
dismiss(animated: true)
// Verification succeeded
// Fetch the final verdict from your backend using flow_id
}func veriliveFailed(result: VeriliveResult) {
dismiss(animated: true)
let errorCode = result.errorCode
let errorMessage = result.errorMessage
// Handle failure
}
}
Handle the result
If verilivePassed is called, you must fetch the final verdict from your backend using the flow_id. The iOS SDK does not provide the liveness verdict directly. This behavior is deliberate; frontend results are not reliable and can be tampered with by an attacker.
To get the results of the Flow, make a GET request to the endpoint /flow/{flow_id}/result:
curl -X GET https://services.<region>.verigram.kz/flow/{flow-id}/result \
-H 'Content-Type: application/json' \
-H 'X-Verigram-Api-Version: 2.0' \
-H 'X-Verigram-Api-Key: your-test-api-key'
Verilive2ViewControllerA UIViewController subclass that hosts the liveness verification flow.
public class Verilive2ViewController: UIViewController {
public var delegate: Verilive2ViewControllerDelegate?
public var params: VeriliveParams?
}
Properties:
delegate: Verilive2ViewControllerDelegate? — Delegate for receiving verification results. Must be set before presenting.
params: VeriliveParams? — Configuration parameters for the verification flow. Must be set before presenting.
onRetry: ((_ sessionId: String, _ errorCode: String, _ attemptsLeft: Int) -> VeriliveRetryResult?)? — Optional. Callback for consumer-managed retry. Called on a background thread after the user taps "Retry". Return new session credentials, or nil to fall back to server-managed retry.
Methods:
cancel() — Cancel the ongoing verification flow. Dismisses the SDK and invokes veriliveFailed(result:) on the delegate with VeriliveResult(success: false, errorCode: "CANCELLED_BY_USER", ...). Use this when the host application needs to programmatically abort verification (e.g., the user taps a cancel button in the host UI outside the SDK modal).
Presentation:
Must be presented modally with .fullScreen presentation style.
Set isModalInPresentation = true to prevent interactive dismissal.
The SDK enforces portrait orientation internally.
Present with animated: false for best UX.
Verilive2ViewControllerDelegateProtocol for receiving verification results.
public protocol Verilive2ViewControllerDelegate: AnyObject {
func verilivePassed(result: VeriliveResult)
func veriliveFailed(result: VeriliveResult)
}
verilivePassed(result:)Called when liveness verification succeeds. The result.success field is always true; errorCode and errorMessage are nil.
veriliveFailed(result:)Called when liveness verification fails, the user cancels, or an error occurs. The result.success field is always false; inspect errorCode for programmatic handling and errorMessage for display.
VeriliveParamsConfiguration for starting a verification flow.
public struct VeriliveParams {
public let baseUrl: String?
public let personId: String
public let accessToken: String
public let flowId: String
public let sessionId: String
public let attemptsLeft: Int?
public init(
baseUrl: String? = nil,
personId: String,
accessToken: String,
flowId: String,
sessionId: String,
attemptsLeft: Int? = nil
)
}
Fields:
baseUrl: String? — Optional. API base URL. Use nil for the default endpoint. Set to your on-premises URL if applicable.
personId: String — The unique identifier for the individual undergoing verification.
accessToken: String — Authentication token obtained from the backend.
flowId: String — The flow identifier for the verification session.
sessionId: String — A unique ID for the current session/attempt.
attemptsLeft: Int? — Optional. Number of retry attempts allowed (default: nil).
VeriliveResultStrongly-typed result returned to the delegate.
public struct VeriliveResult: Equatable {
public let success: Bool
public let errorCode: String?
public let errorMessage: String?
}
Fields:
success: Bool — true if the liveness check passed, false otherwise (failed, cancelled, or errored).
errorCode: String? — Machine-readable error code for programmatic handling. See Error Codes below. nil when success == true.
errorMessage: String? — Human-readable error description. nil when success == true.
VeriliveRetryResultReturn type for the onRetry callback.
public struct VeriliveRetryResult {
public let sessionId: String
public let accessToken: String
public let attemptsLeft: Int
}
To cancel the verification flow from the host application (for example, in response to a user action outside the SDK modal), call cancel() on the Verilive2ViewController instance:
veriliveVC.cancel()
This dismisses the SDK and invokes the delegate's veriliveFailed(result:) method with:
result.success == false
result.errorCode == "CANCELLED_BY_USER"
The SDK does not expose an in-UI cancel button — swipe-to-dismiss is blocked via isModalInPresentation = true — so cancel() is the only way to programmatically abort the flow before it reaches a natural success/failure state.
The SDK supports two retry modes:
When the liveness check fails and attemptsLeft > 0, the SDK automatically shows a retry screen and handles the retry internally via the server endpoint.
let vc = Verilive2ViewController()
vc.params = VeriliveParams(
personId: personId,
accessToken: accessToken,
flowId: flowId,
sessionId: sessionId,
attemptsLeft: 3
)
Set onRetry on the view controller to handle retry logic yourself. The callback is called on a background thread after the user taps "Retry", so you can make synchronous network calls.
let vc = Verilive2ViewController()
vc.params = VeriliveParams(...)
vc.delegate = self
vc.onRetry = { sessionId, errorCode, attemptsLeft in
// Request new session credentials from your backend (runs on background thread)
let semaphore = DispatchSemaphore(value: 0)
var retryResult: VeriliveRetryResult?
var request = URLRequest(url: URL(string: "https://your-api.com/retry-session")!)
request.httpMethod = "POST"
URLSession.shared.dataTask(with: request) { data, _, _ in
// parse response
retryResult = VeriliveRetryResult(
sessionId: newSessionId,
accessToken: newAccessToken,
attemptsLeft: attemptsLeft - 1
)
semaphore.signal()
}.resume()
semaphore.wait()
return retryResult
}
If the callback returns nil, the SDK falls back to server-managed retry.
Errors are communicated through the veriliveFailed delegate method via the VeriliveResult struct. Use errorCode for programmatic handling and errorMessage for display.
Scenario |
|
|
|---|---|---|
Liveness check failed |
| Error description |
Server error |
| Server error description |
Network error |
| Network error description |
SDK initialization error |
| Error description |
Host called |
| "User cancelled the flow" |
Error Code | Description |
|---|---|
| The liveness check did not pass. The user's face was not verified as live. |
| The backend returned an error response during the verification process. |
| A network connectivity issue prevented the verification from completing. |
| An unexpected internal error occurred. |
| The host application called |
Example:
func veriliveFailed(result: VeriliveResult) {
dismiss(animated: true)
switch result.errorCode {
case "LIVENESS_FAILED":
print("Liveness failed: \(result.errorMessage ?? "")")
case "NETWORK_ERROR":
print("Network error: \(result.errorMessage ?? "")")
case "SERVER_ERROR":
print("Server error: \(result.errorMessage ?? "")")
case "CANCELLED_BY_USER":
print("User cancelled")
default:
print("Error: \(result.errorMessage ?? "Unknown")")
}
}
The SDK requires camera access. Add the following to your app's Info.plist:
<key>NSCameraUsageDescription</key>
<string>Camera access is required for liveness verification</string>
The SDK handles camera permission requests internally:
If the user has not been asked yet, the SDK requests permission automatically.
If the user denied camera access, the SDK shows an alert with a link to Settings.
import UIKit
import Verilive2
class MainViewController: UIViewController, Verilive2ViewControllerDelegate {
var veriliveVC: Verilive2ViewController?
func startVerification(
personId: String,
accessToken: String,
flowId: String,
sessionId: String
) {
let vc = Verilive2ViewController()
vc.params = VeriliveParams(
personId: personId,
accessToken: accessToken,
flowId: flowId,
sessionId: sessionId,
attemptsLeft: 3
)
vc.delegate = self
vc.modalPresentationStyle = .fullScreen
vc.isModalInPresentation = true
self.veriliveVC = vc
present(vc, animated: false)
}
// MARK: - Verilive2ViewControllerDelegate
func verilivePassed(result: VeriliveResult) {
dismiss(animated: true) {
// Verification succeeded
// Fetch the final verdict from your backend using flow_id
}
}
func veriliveFailed(result: VeriliveResult) {
dismiss(animated: true) {
let errorCode = result.errorCode ?? "UNKNOWN"
let errorMessage = result.errorMessage ?? ""
// Handle failure using errorCode for programmatic logic
}
}
}