iOS Liveness SDK *New

iOS Compact Version "0.1.3"

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

1

Import the package

import YouverifyLivenessSDK
2

Initialize the SDK

private var yvLiveness = YVLiveness(...options)

For a list of the valid options, check the Options section below.

3

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)
4

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
}
5

Handle closing via delegate

Assign your view controller to the YVLivenessViewDelegate to handle closing:

extension MainViewController: YVLivenessViewDelegate {
    func closeModal() {
        DispatchQueue.main.async {
            self.removeLivenessViewController()
        }
    }
}

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 sessionId and sessionToken and 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 sessionId using the session generation endpoint.

  • Generate Session Token: Call your backend to generate sessionToken using the liveness token endpoint.

  • Initialize SDK: Pass both sessionId and sessionToken to the SDK constructor.

  • SDK Validation: The SDK validates the sessionId before initialization.

  • Error Handling: If validation fails, onFailure is called with key invalid_or_expired_session and session_token_error for both.

  • Success: Upon successful initialization, the SDK uses the sessionToken for 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

Option
Type
Required
Description
Default Value
Possible Values

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

Option
Type
Required
Description
Default Value
Possible Values

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

Option
Type
Required
Description
Default Value
Possible Values

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

Option
Type
Required
Description
Default Value
Possible Values

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

User passes task by blinking their eyelids based on number of times set.

BlinkTaskClass option

Option
Type
Required
Description
Default Value
Possible Values

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.

Option
Type
Required
Description
Default Value
Possible Values

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:

Option
Type
Description

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 Youverifyarrow-up-right

Last updated

Was this helpful?