Skip to content

Liveness

In the liveness step the user records a short video with the front camera. Prove ID checks the video for liveness and compares the face with the document photo.

The gestureType returned when the transaction started decides which gesture the user is asked for.

import UIKit
import RKYC_iOS
final class LivenessViewController: UIViewController {
@IBOutlet weak var containerView: UIView!
private let livenessController = LivenessController()
override func viewDidLoad() {
super.viewDidLoad()
LivenessController.FACE_DETECTION_LAYER_SHAPE = .OVAL
LivenessController.CONTROL_FACE_CAPTURE = true
LivenessController.STOP_VIDEO_WHILE_RECORDING = true
LivenessController.GESTURE_TYPE = .EYE // .EYE, .MOUTH, .HEAD_VERTICAL, .HEAD_HORIZONTAL
LivenessController.IS_GESTURE_CONTROL_ENABLED = true
LivenessController.VIDEO_PREPARATION_TIME_IN_SECONDS = 3
LivenessController.VIDEO_TIME_IN_SECONDS = 5
livenessController.setDelegate(self)
livenessController.setGestureDelegate(self)
containerView.addSubview(livenessController.view)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
livenessController.startCapture()
}
}
extension LivenessViewController: LivenessDelegate, GestureDelegate {
func gestureControlled(gestureModel: GestureModel) {
livenessController.stopCapture()
// Send gestureModel.videoURL and gestureModel.rotate (below)
}
func onVideoCaptured(videoURL: URL, rotate: Int) {
// Called without gesture control (passive liveness)
}
func onVideoStarting() { /* show a countdown */ }
func onVideoStarted() { }
func onVideoCanceled() { /* the face left the frame; ask the user to try again */ }
func onFaceDetectionFailure(isAway: Bool) { /* isAway: move closer, otherwise move away */ }
func onFaceDetectionSuccess() { }
func onFaceDetectionIdle() { }
func onMultiFaceDetected() { /* only one person in the frame */ }
func positionFailed(model: PositionFailedModel) { }
func positionSucceed(model: PositionSuccedModel) { }
func positionCorrect() { }
func preparationPositionControlled(model: PreparationPositionModel) { }
}

gestureType values and their GestureType counterparts:

gestureType LivenessController.GESTURE_TYPE
EYE .EYE
MOUTH .MOUTH
HEAD-VERTICAL .HEAD_VERTICAL
HEAD-HORIZONTAL .HEAD_HORIZONTAL
MULTIDIR_… .RANDOMIZED_MULTI_GESTURE

For head gestures, gestureModel.gestureScore tells you how well the gesture was made; ask the user to try again when it is low (e.g. below 0.8) and call startCapture() again.

A gestureType starting with MULTIDIR_ asks the user to turn their head in several directions in a row. The directions follow the prefix, separated by -; pass them to the SDK as HeadPosition values:

let directions = gestureType.dropFirst("MULTIDIR_".count).split(separator: "-")
LivenessController.GESTURE_TYPE = .RANDOMIZED_MULTI_GESTURE
LivenessController.RANDOMIZED_GESTURES = directions.compactMap { HeadPosition(string: String($0)) }
LivenessController.IS_GESTURE_CONTROL_ENABLED = true
LivenessController.VIDEO_PREPARATION_TIME_IN_SECONDS = 0
import ProveIDApiClient
guard let video = try? Data(contentsOf: gestureModel.videoURL) else { return }
let body = FaceVerificationModel(transactionId: transactionId,
video: video.base64EncodedString(),
rotate: gestureModel.rotate)
FaceServiceAPI.verifyFace(body: body) { result in
switch result {
case .success(let response):
let controls = response.controlResults
showLivenessResult(controls)
case .failure(let error):
showError(error)
}
}
try? FileManager.default.removeItem(at: gestureModel.videoURL) // don't leave the video on the device

Works without a gesture, with a short recording. The video arrives in onVideoCaptured(videoURL:rotate:); send it the same way.

LivenessController.IS_GESTURE_CONTROL_ENABLED = false
LivenessController.VIDEO_PREPARATION_TIME_IN_SECONDS = 1
LivenessController.VIDEO_TIME_IN_SECONDS = 2

With a single photo instead of a video:

LivenessController.LIVENESS_FROM_IMAGE_ENABLED = true
livenessController.setLivenessPhotoDelegate(self)
extension LivenessViewController: LivenessPhotoDelegate {
func photoCaptured(image: UIImage) {
livenessController.stopCapture()
let body = FaceVerificationWithImageInputModel(transactionId: transactionId,
image: image.pngData()!.base64EncodedString())
FaceServiceAPI.verifyFaceWithImage(body: body) { result in /* same as verifyFace */ }
}
func eligiblePositionForCapture(model: PreparationPositionModel) { }
}

With the LivenessWithCard module the user’s face and the front of their ID card are captured together, and the face is matched with the card photo.

import LivenessWithCard
LWICController.CARD_CAPTURE_ENABLED = true
LWICController.LIVENESS_FROM_IMAGE_ENABLED = true
LWICController.FACE_DETECTION_LAYER_SHAPE = .OVAL
let lwicController = LWICController()
lwicController.cardType = .NEW_ID
lwicController.setDelegate(self)
lwicController.setCardDelegate(self)
lwicController.setPhotoDelegate(self)
containerView.addSubview(lwicController.view)
lwicController.startCapture()

Send the captured image:

let body = CheckLivenessWithCardImageInputModel(transactionId: transactionId,
imageBase64: image.pngData()!.base64EncodedString(),
livenessProcessStage: "FACEANDFRONT")
IDServiceAPI.checkLivenessWithIdCardImage(body: body) { result in /* … */ }

Start the transaction with the LIVENESS_WithIdCard control for this step. The face match is reported in the LWICONLYFRONTFACESIMILARITY control.

Setting Description
FACE_DETECTION_LAYER_SHAPE Shape of the face frame (.OVAL)
CONTROL_FACE_CAPTURE Records only while a face is in the frame
STOP_VIDEO_WHILE_RECORDING Cancels the recording if the face leaves the frame
FACE_OVER_SCREEN_RATIO Size of the face frame relative to the screen (e.g. 0.6)
FACE_AWAY_CHECK_RATIO How far the face may be before isAway is reported
GESTURE_TYPE Gesture to ask for
IS_GESTURE_CONTROL_ENABLED Checks the gesture on the device
HEAD_HORIZONTAL_MAX_PREP_THRESHOLD, HEAD_VERTICAL_MAX_PREP_THRESHOLD Head angle allowed before recording starts (e.g. 15)
VIDEO_PREPARATION_TIME_IN_SECONDS Countdown before recording
VIDEO_TIME_IN_SECONDS Recording length
LIVENESS_FROM_IMAGE_ENABLED Takes a photo instead of a video