iOS Liveness SDK *New
iOS Compact Version "0.1.5"
Our iOS SDK enables seamless integration of real-time liveness detection into your mobile applications. To get started with our SDK, follow the guide below:
Installation
To install, run:
pod 'YouverifyLivenessSDKCompat'Usage
Start the liveness process
Start with tasks provided during initialization (or pass an array of tasks to override):
yvLiveness.startSDK(tasks: tasks)
// add the liveness view controller to the current view controller
DispatchQueue.main.async {
self.addLivenessViewController()
}Example with an explicit task array:
let livenessTasks = [
TaskProperties(task:
YVTask.completeTheCircle(CompleteTheCircleTask(difficulty: .medium))
)
]
yvLiveness.startSDK(tasks: livenessTasks)Add and remove the liveness view controller
private var livenessViewController: YVLivenessViewController?
private func addLivenessViewController() {
guard livenessViewController == nil else { return } // Prevent duplicate adds
let vc = YVLivenessViewController(sdk: yvLiveness)
vc.delegate = self
addChild(vc)
view.addSubview(vc.view)
vc.didMove(toParent: self)
livenessViewController = vc
}
private func removeLivenessViewController() {
guard let vc = livenessViewController else { return }
vc.willMove(toParent: nil)
vc.view.removeFromSuperview()
vc.removeFromParent()
livenessViewController = nil
}Full Example
import YouverifyLivenessSDK
class MainViewController: UIViewController {
var yvLiveness: YVLiveness?
private var livenessViewController: YVLivenessViewController?
override func viewDidLoad() {
super.viewDidLoad()
initializeSDK()
// Create a vertical stack view
let stackView = UIStackView()
stackView.axis = .vertical
stackView.spacing = 10
stackView.translatesAutoresizingMaskIntoConstraints = false
// Create buttons
let completeCircleButton = createButton(title: "Complete the Circle", action: #selector(completeTheCircleTapped))
// Add buttons to stack view
stackView.addArrangedSubview(completeCircleButton)
// Add stack view to the main view
view.addSubview(stackView)
// Set Auto Layout constraints
NSLayoutConstraint.activate([
stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20)
])
}
private func initializeSDK() {
// start loading...
ApiClient.shared.generateSessionId(publicMerchantID: publicMerchantID,
apiToken: apiToken) { [weak self] result in
switch result {
case .success(let sessionId):
// Now get token
ApiClient.shared.generateSessionToken(publicMerchantID: publicMerchantID,
deviceCorrelationId: sessionId,
apiToken: apiToken) { result in
switch result {
case .success(let authToken):
// end loading
DispatchQueue.main.async {
self?.yvLiveness = YVLiveness(
publicKey: publicMerchantID,
user: YVLivenessUser(firstName: "John"),
onSuccess: { data in
print("The success data is \(data)")
},
onFailure: { errorData in
if let error = errorData.error {
if error.key == "invalid_or_expired_session" {
print("Session expired, retrying...")
self?.initializeSDK()
} else if error.key == "session_token_error" {
print("Session token error, retrying...")
self?.initializeSDK()
}
}
},
sessionId: sessionId,
sessionToken: authToken
)
}
case .failure(let error):
print("Token failed:", error)
}
}
case .failure(let error):
print("Session failed:", error)
}
}
}
private func addLivenessViewController() {
guard livenessViewController == nil else { return } // Prevent duplicate adds
guard let yvLiveness = yvLiveness else { return }
let vc = YVLivenessViewController(sdk: yvLiveness)
vc.delegate = self
addChild(vc)
view.addSubview(vc.view)
vc.didMove(toParent: self)
livenessViewController = vc
}
private func removeLivenessViewController() {
guard let vc = livenessViewController else { return }
vc.willMove(toParent: nil)
vc.view.removeFromSuperview()
vc.removeFromParent()
livenessViewController = nil
}
// Helper function to create buttons with actions
private func createButton(title: String, action: Selector) -> UIButton {
let button = UIButton(type: .system)
button.setTitle(title, for: .normal)
button.addTarget(self, action: action, for: .touchUpInside)
return button
}
private func startLiveness(with tasks: [TaskProperties]) {
guard let yvLiveness = yvLiveness else { return }
yvLiveness.startSDK(tasks: tasks)
DispatchQueue.main.async {
self.addLivenessViewController()
}
}
@objc private func completeTheCircleTapped() {
startLiveness(with: [
TaskProperties(task: .completeTheCircle(CompleteTheCircleTask(difficulty: .medium)))
])
}
}
extension MainViewController: YVLivenessViewDelegate {
func closeModal() {
DispatchQueue.main.async {
self.removeLivenessViewController()
}
}
}Breaking change: session token generation now external
The SDK no longer generates session tokens internally.
Partners must call their backend to generate both
sessionIdandsessionTokenand pass them to the SDK via the respective options.
Base URL Configuration
All API endpoints use the following base URL:
Sandbox base URL:
Live base URL:
Session ID Generation
Before initializing the SDK, you must generate a sessionId by calling your backend API. Your backend should make the following request:
Endpoint: POST /v2/api/identity/sdk/session/generate
Headers:
Content-Type: "application/json"
Token: "YOUR_API_KEY"
Request Body:
publicMerchantID: "your_public_merchant_id",
metadata": [:]
Response:
Session Token Generation
Additionally, you need to generate a sessionToken for liveness verification.
Endpoint: POST /v2/api/identity/sdk/liveness/token
Headers:
Content-Type: "application/json",
Token: "YOUR_API_KEY"
Request Body:
publicMerchantID: "your_public_merchant_id",
deviceCorrelationId: "unique_device_identifier"
Response:
The authToken from the response should be passed as sessionToken to the SDK constructor.
Complete Integration Flow
Generate Session ID: Call your backend to generate
sessionIdusing the session generation endpoint.Generate Session Token: Call your backend to generate
sessionTokenusing the liveness token endpoint.Initialize SDK: Pass both
sessionIdandsessionTokento the SDK constructor.SDK Validation: The SDK validates the
sessionIdbefore initialization.Error Handling: If validation fails,
onFailureis called with keyinvalid_or_expired_sessionandsession_token_errorfor both.Success: Upon successful initialization, the SDK uses the
sessionTokenfor liveness verification.
Error Keys
invalid_or_expired_session: Returned when the sessionId is invalid or expired
session_token_error: Returned when there's an issue with the sessionToken during liveness verification
Retry Logic
If session validation fails, generate a new sessionId and retry.
If liveness fails, users may retry while the current sessionId remains valid.
If the sessionId expires, create a new session and restart the entire process.
Token generation
Here's an example of the ApiClient class housing the generateSessionId and the generateSessionToken.
Options
publicKey
String
No
Your Youverify Public Merchant Key
nil
Valid Youverify Public Key
sandboxEnvironment
Bool
No
Sets whether session should run in sandbox or live mode
true
true, false
tasks
Array
No
Sets tasks that need to be performed for liveness to be confirmed
nil
See tasks section
user
YVLivenessUser
No
Sets details of user for which liveness check is being performed
nil
See nested options below
user.firstName
String
Yes
First name of user
-
Any string
user.lastName
String
No
Last name of user
nil
Any string
user.email
String
No
Email of user
nil
Any string
branding
Branding
No
Customizes UI to fit your brand
nil
See nested options below
branding.name
String
No
The name of the brand
nil
Any string
branding.color
String
No
Sets your branding color
nil
Valid hex string
branding.logo
String
No
Sets your logo
nil
Valid image link
branding.logoAlt
String
No
Alternative text for the brand logo
"Youverify"
Any string
branding.hideLogoOnMobile
Bool
No
Hides logo in mobile webview
false
true, false
branding.showPoweredBy
Bool
No
Displays "Powered by" footer text
false
true, false
branding.poweredByText
String
No
Customizes the "Powered by" text
"Powered by"
Any string
branding.poweredByLogo
String
No
Customizes the "Powered by" logo
nil
Valid image link
allowAudio
Bool
No
Sets whether to narrate information to user during tasks
false
true, false
onClose
Function
No
Callback function that gets triggered when modal is closed
nil
Any valid function
onSuccess
Function
No
Callback function that gets triggered when all tasks have been completed and passed. Called with completion data (see Liveness Data)
nil
Any valid function
onFailure
Function
No
Callback function that gets triggered when at least one task fails. Called with completion data (see Liveness Data)
nil
Any valid function
sessionId
String
Yes (required)
ID generated by your backend using your API key. Validated before SDK init and attached to submissions
-
Any valid session ID
sessionToken
String
Yes (required)
Token generated by your backend for liveness verification
-
Any valid session token
Tasks
A task is a series of instructions for users to follow to confirm liveness. We aim to frequently add to this list.
Complete The Circle
User passes task by completing imaginary circle with head movement.
CompleteTheCircleTask option
difficulty
TaskDifficulty
No
Sets difficulty of task
.medium
.easy, .medium, .hard
timeout
Number
No
Sets time in milliseconds after which task automatically fails
nil
Any number in milliseconds
Yes Or No
User passes task by answering a list of arbitrary questions set by you with the tilting of the head; right for a yes and left for a no.
YesOrNoTask option
difficulty
TaskDifficulty
No
Sets difficulty of task
.medium
.easy, .medium, .hard
timeout
Number
No
Sets time in milliseconds after which task automatically fails
nil
Any number in milliseconds
questions
Array
No
A set of questions to ask user
nil
See nested options below
questions.question
String
Yes
Question to ask user. Should be a true or false type question. Eg: "Are you ready"
-
Any string
questions.answer
Bool
Yes
Answer to the question
-
true, false
questions.errorMessage
String
No
Error message to display if user gets question wrong
nil
Any string
Motions
User passes task by performing random motions in random sequences, which include nodding, blinking and opening of mouth.
MotionsTaskClass option
difficulty
TaskDifficulty
No
Sets difficulty of task
.medium
.easy, .medium, .hard
timeout
TimeInterval
No
Sets time in milliseconds after which task automatically fails
nil
Any number in milliseconds
maxNods
Int
No
Maximum amount of nods a user is asked to perform
5
Any number
maxBlinks
Int
No
Maximum amount of nods a user is asked to perform
5
Any number
Blink
User passes task by blinking their eyelids based on number of times set.
BlinkTaskClass option
difficulty
TaskDifficulty
No
Sets difficulty of task
.medium
.easy, .medium, .hard
timeout
TimeInterval
No
Sets time in milliseconds after which task automatically fails
nil
Any number in milliseconds
maxBlinks
Int
No
Maximum amount of nods a user is asked to perform
5
Any number
TaskProperties
User passes in the class and preferred settings for each task.
task
YVTask
Yes
Id of task
-
YVTask.completeTheCircle(Task_class eg. CompleteTheCircleTask)
timeout
TimeInterval
No
Sets time in milliseconds after which task automatically fails
nil
Any number in milliseconds
Liveness Data
The onSuccess and onFailure callbacks (if supplied) are passed the following data:
data
LivenessData
Data passed through callback
data.faceImage
String
Face Image of user performing liveness check
data.livenessClip
String
Video of user performing liveness check
data.passed
Bool
Indicator on whether liveness check passed or failed
data.metadata
Any
Metadata passed in during initialization
data.error
LivenessError
Error object containing error key (eyes_closed, image_capture_failed, API_ERROR) and message (only present in failure cases)
Credits
This SDK is developed and maintained solely by Youverify
Last updated
Was this helpful?
