Skip to content

Liveness

Display Liveness

Liveness Assessment comes with the configuration allowing you to change the behavior and appearance of some UI elements. For more UI customization options, check out the UI Customization section.

Starting the Liveness process with configuration:

let configuration = LivenessConfiguration { _ in

}

FaceSDK.service.startLiveness(
    from: viewController,
    animated: true, 
    configuration: configuration,
    onLiveness: { response in
        // ... check response.liveness for detection result.
    },
    completion: nil
)
RFSLivenessConfiguration *configuration = [RFSLivenessConfiguration configurationWithBuilder:^(RFSLivenessConfigurationBuilder  * _Nonnull builder) {

}];

[RFSFaceSDK.service startLivenessFrom:viewController
                             animated:YES
                        configuration:configuration
                           onLiveness:^(RFSLivenessResponse * _Nonnull response) {
    // ... check response.liveness for detection result.
} completion:nil];
val configuration = LivenessConfiguration.Builder()
    .build()

FaceSDK.Instance().startLiveness(this@MainActivity, configuration) { response ->
    // ... check response.getLiveness(); for detection result.
}
LivenessConfiguration configuration = new LivenessConfiguration.Builder()
    .build();

FaceSDK.Instance().startLiveness(MainActivity.this, configuration, livenessResponse -> {
    // ... check livenessResponse.liveness for detection result.
});
FaceSDK.startLiveness(null, (json) => {
  var response = LivenessResponse.fromJson(JSON.parse(json))
  // ... check response.liveness for detection result.
}, _ => { })
var response = await FaceSDK.instance.startLiveness();
// ... check response.liveness for detection result.
FaceSDK.startLiveness(null, raw => {
    // handling events:
    var lnEventId = "livenessNotificationEvent"
    var csEventId = "cameraSwitchEvent"
    if (raw.substring(0, lnEventId.length) === lnEventId) {
        raw = raw.substring(lnEventId.length, raw.length)
        var notification = LivenessNotification.fromJson(JSON.parse(raw))
        // handle liveness notification
    } else if (raw.substring(0, csEventId.length) === csEventId) {
        raw = raw.substring(csEventId.length, raw.length)
        var cameraId = raw
        // handle new camera id
    } else {
        var response = LivenessResponse.fromJson(JSON.parse(raw))
        // ... check response.liveness for detection result.
    }
}, e => { })
FaceSDK.startLiveness(null).subscribe((raw: string) => {
  // handling events:
  var lnEventId = "livenessNotificationEvent"
  var csEventId = "cameraSwitchEvent"
  if (raw.substring(0, lnEventId.length) === lnEventId) {
    raw = raw.substring(lnEventId.length, raw.length)
    var notification = LivenessNotification.fromJson(JSON.parse(raw))
    // handle liveness notification
  } else if (raw.substring(0, csEventId.length) === csEventId) {
    raw = raw.substring(csEventId.length, raw.length)
    var cameraId = raw
    // handle new camera id
  } else {
    var response = LivenessResponse.fromJson(JSON.parse(raw))
    // ... check response.liveness for detection result.
  }
})
// Android
LivenessConfiguration configuration = new LivenessConfiguration.Builder().Build();
FaceSDK.Instance().StartLiveness(Platform.AppContext, configuration, this);

// iOS
RFSFaceSDK.Service.StartLivenessFrom(CurrentPresenter, true, RFSLivenessConfiguration.ConfigurationWithBuilder((RFSLivenessConfigurationBuilder builder) =>
{

}), (RFSLivenessResponse response) =>
{

}, () => { });

To keep the default configuration, you can omit the configuration parameter.

To stop the Liveness processing programmatically, there is a call:

FaceSDK.service.stopLivenessProcessing()
[RFSFaceSDK.service stopLivenessProcessing];
FaceSDK.Instance().stopLivenessProcessing(this@MainActivity)
FaceSDK.Instance().stopLivenessProcessing(MainActivity.this);
FaceSDK.stopLiveness(_ => {}, _ => {});
FaceSDK.instance.stopLiveness();
FaceSDK.stopLiveness(function (m) { }, function (e) { });
FaceSDK.stopLiveness();
// Android
FaceSDK.Instance().StopLivenessProcessing(Platform.AppContext);

// iOS
RFSFaceSDK.Service.StopLivenessProcessing();

Tag

The detailed tag description is available at Liveness Check on Web Service.

To set up tag, invoke:

let configuration = LivenessConfiguration { 
    $0.tag = UUID().uuidString
}

FaceSDK.service.startLiveness(
    from: viewController,
    animated: true, 
    configuration: configuration,
    onLiveness: { response in

    },
    completion: nil
)
RFSLivenessConfiguration *configuration = [RFSLivenessConfiguration configurationWithBuilder:^(RFSLivenessConfigurationBuilder  * _Nonnull builder) {
    builder.tag = [[NSUUID UUID] UUIDString];
}];

[RFSFaceSDK.service startLivenessFrom:viewController
                             animated:YES
                        configuration:configuration
                           onLiveness:^(RFSLivenessResponse * _Nonnull response) {

} completion:nil];
val configuration = LivenessConfiguration.Builder()
            .setTag(UUID.randomUUID().toString())
            .build()
FaceSDK.Instance().startLiveness(context, configuration) { livenessResponse: LivenessResponse? -> }
LivenessConfiguration configuration = new LivenessConfiguration.Builder()
            .setTag(UUID.randomUUID().toString())
            .build();
FaceSDK.Instance().startLiveness(context, configuration, livenessResponse -> {});
FaceSDK.startLiveness({
  tag: ""
}, _ => { }, _ => { })
FaceSDK.instance.startLiveness(
  config: LivenessConfig(
    tag: "",
  ),
);
FaceSDK.startLiveness({
  tag: ""
}, function (m) { }, function (e) { })
FaceSDK.startLiveness({
  tag: ""
})
// Android
LivenessConfiguration configuration = new LivenessConfiguration.Builder()
    .SetTag(UUID.RandomUUID().ToString())
    .Build();
FaceSDK.Instance().StartLiveness(Platform.AppContext, configuration, this);

// iOS
RFSFaceSDK.Service.StartLivenessFrom(CurrentPresenter, true, RFSLivenessConfiguration.ConfigurationWithBuilder((RFSLivenessConfigurationBuilder builder) =>
{
    builder.Tag = "uuid string";
}), (RFSLivenessResponse response) =>
{

}, () => { });

For details about the status of the session video upload to the service, see Session Video Upload Status.

Passive and Active Liveness

Active liveness check requires users to turn their head and adjust their face, while passive authentication simply involves taking a selfie. Find more details on the Introduction page.

By default, active liveness check is enabled. To turn on passive liveness, invoke:

let configuration = LivenessConfiguration {
    $0.livenessType = .passive
}

FaceSDK.service.startLiveness(from: self, animated: true, configuration: configuration) { response in

} completion: {

}
RFSLivenessConfiguration *configuraion = [RFSLivenessConfiguration configurationWithBuilder:^(RFSLivenessConfigurationBuilder * _Nonnull builder) {
    builder.livenessType = RFSLivenessTypePassive;
}];

[RFSFaceSDK.service startLivenessFrom:self animated:YES configuration:configuraion onLiveness:^(RFSLivenessResponse * _Nonnull response) {

} completion:^{

}];
val configuration: LivenessConfiguration = LivenessConfiguration.Builder()
        .setType(LivenessType.PASSIVE)
        .build();
LivenessConfiguration configuration = new LivenessConfiguration.Builder()
        .setType(LivenessType.PASSIVE)
        .build();
FaceSDK.startLiveness({
  livenessType: Enum.LivenessType.PASSIVE
}, _ => { }, _ => { })
FaceSDK.instance.startLiveness(
  config: LivenessConfig(
    livenessType: LivenessType.PASSIVE,
  ),
);
FaceSDK.startLiveness({
  livenessType: Enum.LivenessType.PASSIVE
}, function (m) { }, function (e) { })
FaceSDK.startLiveness({
  livenessType: Enum.LivenessType.PASSIVE
})
// Android
LivenessConfiguration.Builder config = new();
config.SetType(LivenessType.Passive);
FaceSDK.Instance().StartLiveness(Platform.AppContext, config.Build(), this);

// iOS
RFSFaceSDK.Service.StartLivenessFrom(CurrentPresenter, true, RFSLivenessConfiguration.ConfigurationWithBuilder((RFSLivenessConfigurationBuilder builder) =>
{
    builder.LivenessType = RFSLivenessType.Passive;
}), (RFSLivenessResponse response) =>
{
}, () => { });

Response

Status

LivenessResponse contains the response.json file, where you can find livenessStatus with the result of processing: 0 if the person's liveness is confirmed, 1 if not. Also, the errors may be returned.

To determine the result of the detection, check the liveness property:

if case response.liveness = .passed {
    // continue with successful path.
}
if (response.liveness == RFSLivenessStatusPassed) {
    // continue with successful path.
}
Log.d("liveness", "Session id: " + response.tag)
Log.d("liveness", "Transaction id: " + response.transactionId)
Log.d("liveness", "Session id: " + response.getTag());
Log.d("liveness", "Transaction id: " + response.getTransactionId());
if (response.liveness == Enum.LivenessStatus.PASSED) {
  // continue with successful path.
}
if (response.liveness == LivenessStatus.PASSED) {
  // continue with successful path.
}
if (response.liveness == Enum.LivenessStatus.PASSED) {
  // continue with successful path.
}
if (response.liveness == Enum.LivenessStatus.PASSED) {
  // continue with successful path.
}
// Android
Console.WriteLine("Session id: " + response.Tag);
Console.WriteLine("Transaction id: " + response.TransactionId);

// iOS
if (response.Liveness == RFSLivenessStatus.Passed)
{
    // continue with successful path.
}

UI Customization

A number of UI elements of the liveness assessment session can be customized to suit your application's unique requirements. See Liveness Assessment Session UI Customization for details.

To add text and graphic elements to the camera UI, follow the guides:

Error Handling

The errors and exceptions give you an insight into what happened to the Liveness processing from a user canceling the operation to a missing license on a web service.

Here is an example of how you can handle a failed LivenessResponse:

if let error = response.error {
    // There is an error. Lets see what type is it.
    switch error {
        case LivenessError.cancelled:
            Log.d("User cancelled the processing.")
        case LivenessError.noLicense:
            Log.d("Web Service is missing a valid License.")
        case LivenessError.processingAttemptsEnded:
            Log.d("Reached the number of possible attempts. See `RFSLivenessConfiguration.attemptsCount` for more information.")
        case LivenessError.processingFailed:
            Log.d("Bad input data.")
        case LivenessError.processingTimeout:
            Log.d("Processing finished by timeout.")
        case LivenessError.apiCallFailed:
            Log.d("Web Service API call failed due to networking error or backend internal error.")
        default:
            break
    }
}
if (response.error) {
    // There is an error. Lets see what type is it.
    switch ((RFSLivenessError)response.error.code) {
        case RFSLivenessErrorCancelled:
            NSLog(@"User cancelled the processing.");
            break;
        case RFSLivenessErrorNoLicense:
            NSLog(@"Web Service is missing a valid License.");
            break;
        case RFSLivenessErrorProcessingAttemptsEnded:
            NSLog(@"Reached the number of possible attempts. See `RFSLivenessConfiguration.attemptsCount` for more information.");
            break;
        case RFSLivenessErrorProcessingFailed:
            NSLog(@"Bad input data.");
            break;
        case RFSLivenessErrorProcessingTimeout:
            NSLog(@"Processing finished by timeout.");
            break;
        case RFSLivenessErrorAPICallFailed:
            NSLog(@"Web Service API call failed due to networking error or backend internal error.");
            break;
    }
}
val exception = response.exception
if (exception != null) {
    when (exception.errorCode) {
        LivenessErrorCode.CANCELLED ->
            Log.d("User cancelled the processing.")
        LivenessErrorCode.NO_LICENSE ->
            Log.d("Web Service is missing a valid License.")
        LivenessErrorCode.PROCESSING_ATTEMPTS_ENDED ->
            Log.d("Reached the number of possible attempts. See `LivenessConfiguration.attemptsCount` for more information.")
        LivenessErrorCode.PROCESSING_FAILED ->
            Log.d("Bad input data.")
        LivenessErrorCode.PROCESSING_TIMEOUT ->
            Log.d("Processing finished by timeout.")
        LivenessErrorCode.API_CALL_FAILED ->
            Log.d("Web Service API call failed due to networking error or backend internal error.")
        LivenessErrorCode.CONTEXT_IS_NULL ->
            Log.d("Provided context is null.")
        LivenessErrorCode.IN_PROGRESS_ALREADY ->
            Log.d("Liveness has already started.")
        LivenessErrorCode.ZOOM_NOT_SUPPORTED ->
            Log.d("Camera zoom support is required.")
        LivenessErrorCode.CAMERA_NO_PERMISSION ->
            Log.d("Application does not have camera permission.")
        LivenessErrorCode.CAMERA_NOT_AVAILABLE ->
            Log.d("Device has no available camera.")
        LivenessErrorCode.PROCESSING_FRAME_FAILED ->
            Log.d("Failed when Core could not recognize frame.")
    }
}
LivenessErrorException exception = response.getException();
if (exception != null) {
    switch (exeption.getErrorCode()) {
        case LivenessErrorCode.CANCELLED:
            Log.d("liveness", "User cancelled the processing.");
            break;
        case LivenessErrorCode.NO_LICENSE:
            Log.d("liveness", "Web Service is missing a valid License.");
            break;
        case LivenessErrorCode.PROCESSING_ATTEMPTS_ENDED:
            Log.d("liveness", "Reached the number of possible attempts. See `LivenessConfiguration.attemptsCount` for more information.");
            break;
        case LivenessErrorCode.PROCESSING_FAILED:
            Log.d("liveness", "Bad input data.");
            break;
        case LivenessErrorCode.PROCESSING_TIMEOUT:
            Log.d("liveness", "Processing finished by timeout.");
            break;
        case LivenessErrorCode.API_CALL_FAILED:
            Log.d("liveness", "Web Service API call failed due to networking error or backend internal error.");
            break;
        case LivenessErrorCode.CONTEXT_IS_NULL:
            Log.d("liveness", "Provided context is null.");
            break;
        case LivenessErrorCode.IN_PROGRESS_ALREADY:
            Log.d("liveness", "Liveness has already started.");
            break;
        case LivenessErrorCode.ZOOM_NOT_SUPPORTED:
            Log.d("liveness", "Camera zoom support is required.");
            break;
        case LivenessErrorCode.CAMERA_NO_PERMISSION:
            Log.d("liveness", "Application does not have camera permission.");
            break;
        case LivenessErrorCode.CAMERA_NOT_AVAILABLE:
            Log.d("liveness", "Device has no available camera.");
            break;
        case LivenessErrorCode.PROCESSING_FRAME_FAILED:
            Log.d("liveness", "Failed when Core could not recognize frame.");
            break;
    }
}
var error = response.error;
if (error != null)
  switch (error.code) {
    case Enum.LivenessErrorCode.CANCELLED:
      console.log('liveness: User cancelled the processing.');
      break;
    case Enum.LivenessErrorCode.NO_LICENSE:
      console.log('liveness: Web Service is missing a valid License.');
      break;
    case Enum.LivenessErrorCode.PROCESSING_FAILED:
      console.log('liveness: Bad input data.');
      break;
    case Enum.LivenessErrorCode.PROCESSING_TIMEOUT:
      console.log('liveness: Processing finished by timeout.');
      break;
    case Enum.LivenessErrorCode.API_CALL_FAILED:
      console.log('liveness: Web Service API call failed due to networking error or backend internal error.');
      break;
    case Enum.LivenessErrorCode.CONTEXT_IS_NULL:
      console.log('liveness: Provided context is null.');
      break;
    case Enum.LivenessErrorCode.IN_PROGRESS_ALREADY:
      console.log('liveness: Liveness has already started.');
      break;
    case Enum.LivenessErrorCode.ZOOM_NOT_SUPPORTED:
      console.log('liveness: Camera zoom support is required.');
      break;
  }
var error = response.error;
if (error != null)
  switch (error.code) {
    case LivenessErrorCode.CANCELLED:
      print('liveness: User cancelled the processing.');
      break;
    case LivenessErrorCode.NO_LICENSE:
      print('liveness: Web Service is missing a valid License.');
      break;
    case LivenessErrorCode.PROCESSING_FAILED:
      print('liveness: Bad input data.');
      break;
    case LivenessErrorCode.PROCESSING_TIMEOUT:
      print('liveness: Processing finished by timeout.');
      break;
    case LivenessErrorCode.API_CALL_FAILED:
      print('liveness: Web Service API call failed due to networking error or backend internal error.');
      break;
    case LivenessErrorCode.CONTEXT_IS_NULL:
      print('liveness: Provided context is null.');
      break;
    case LivenessErrorCode.IN_PROGRESS_ALREADY:
      print('liveness: Liveness has already started.');
      break;
    case LivenessErrorCode.ZOOM_NOT_SUPPORTED:
      print('liveness: Camera zoom support is required.');
      break;
    default:
      print('liveness: error.');
      break;
  }
var error = response.error;
if (error != null)
  switch (error.code) {
    case Enum.LivenessErrorCode.CANCELLED:
      console.log('liveness: User cancelled the processing.');
      break;
    case Enum.LivenessErrorCode.NO_LICENSE:
      console.log('liveness: Web Service is missing a valid License.');
      break;
    case Enum.LivenessErrorCode.PROCESSING_FAILED:
      console.log('liveness: Bad input data.');
      break;
    case Enum.LivenessErrorCode.PROCESSING_TIMEOUT:
      console.log('liveness: Processing finished by timeout.');
      break;
    case Enum.LivenessErrorCode.API_CALL_FAILED:
      console.log('liveness: Web Service API call failed due to networking error or backend internal error.');
      break;
    case Enum.LivenessErrorCode.CONTEXT_IS_NULL:
      console.log('liveness: Provided context is null.');
      break;
    case Enum.LivenessErrorCode.IN_PROGRESS_ALREADY:
      console.log('liveness: Liveness has already started.');
      break;
    case Enum.LivenessErrorCode.ZOOM_NOT_SUPPORTED:
      console.log('liveness: Camera zoom support is required.');
      break;
  }
var error = response.error;
if (error != null)
  switch (error.code) {
    case Enum.LivenessErrorCode.CANCELLED:
      console.log('liveness: User cancelled the processing.');
      break;
    case Enum.LivenessErrorCode.NO_LICENSE:
      console.log('liveness: Web Service is missing a valid License.');
      break;
    case Enum.LivenessErrorCode.PROCESSING_FAILED:
      console.log('liveness: Bad input data.');
      break;
    case Enum.LivenessErrorCode.PROCESSING_TIMEOUT:
      console.log('liveness: Processing finished by timeout.');
      break;
    case Enum.LivenessErrorCode.API_CALL_FAILED:
      console.log('liveness: Web Service API call failed due to networking error or backend internal error.');
      break;
    case Enum.LivenessErrorCode.CONTEXT_IS_NULL:
      console.log('liveness: Provided context is null.');
      break;
    case Enum.LivenessErrorCode.IN_PROGRESS_ALREADY:
      console.log('liveness: Liveness has already started.');
      break;
    case Enum.LivenessErrorCode.ZOOM_NOT_SUPPORTED:
      console.log('liveness: Camera zoom support is required.');
      break;
  }
// Android
LivenessErrorException exception = response.Exception;
if (exception != null) {
    switch (exception.ErrorCode) {
        case var value when value == LivenessErrorCode.Cancelled:
            Console.WriteLine("User cancelled the processing.");
            break;
        case var value when value == LivenessErrorCode.NoLicense:
            Console.WriteLine("Web Service is missing a valid License.");
            break;
        case var value when value == LivenessErrorCode.ProcessingFailed:
            Console.WriteLine("Bad input data.");
            break;
        case var value when value == LivenessErrorCode.ProcessingTimeout:
            Console.WriteLine("Processing finished by timeout.");
            break;
        case var value when value == LivenessErrorCode.ApiCallFailed:
            Console.WriteLine("Web Service API call failed due to networking error or backend internal error.");
            break;
        case var value when value == LivenessErrorCode.ContextIsNull:
            Console.WriteLine("Provided context is null.");
            break;
        case var value when value == LivenessErrorCode.InProgressAlready:
            Console.WriteLine("Liveness has already started.");
            break;
        case var value when value == LivenessErrorCode.ZoomNotSupported:
            Console.WriteLine("Camera zoom support is required.");
            break;
        case var value when value == LivenessErrorCode.CameraNoPermission:
            Console.WriteLine("Application does not have camera permission.");
            break;
        case var value when value == LivenessErrorCode.CameraNotAvailable:
            Console.WriteLine("Device has no available camera.");
            break;
        case var value when value == LivenessErrorCode.ProcessingFrameFailed:
            Console.WriteLine("Failed when Core could not recognize frame.");
            break;
    }
}

// iOS
if (response.Error != null) {
    // There is an error. Lets see what type is it.
    switch ((RFSLivenessErrorCode)response.Error.Code) {
        case RFSLivenessErrorCode.Cancelled:
            Console.WriteLine("User cancelled the processing.");
            break;
        case RFSLivenessErrorCode.NoLicense:
            Console.WriteLine("Web Service is missing a valid License.");
            break;
        case RFSLivenessErrorCode.ProcessingFailed:
            Console.WriteLine("Bad input data.");
            break;
        case RFSLivenessErrorCode.ProcessingTimeout:
            Console.WriteLine("Processing finished by timeout.");
            break;
        case RFSLivenessErrorCode.APICallFailed:
            Console.WriteLine("Web Service API call failed due to networking error or backend internal error.");
            break;
    }
}

Info

For more information about LivenessConfiguration and LivenessResponse, see the SDK Reference page.