Documentation

Integration(React Native)

Integration Documentation for React Native Compatible with Liveness Detection V4.x Version.

Overview-Android

  • Minimum Android version:4.4 (API Level:19)
  • Compilation Android SDK version:API Level:35
  • Target Android SDK version:API Level:35
  • Supported CPU architectures:armeabi-v7a,arm64-v8a
  • SDK incremental package size:4.5MB+

    If you have requirements for package size, you can refer to this to reduce the package size by approximately 1.4MB.

  • Capture image size:default capture image resolution 600px*600px, size is about 300KB, support custom image size range: 300px~1000px
  • Supported languages:
    • English
    • Indonesian
    • Vietnamese
    • Chinese
    • Hendi
    • Thai
    • Spanish
  • Use-permissions:
    <uses-feature android:name="android.hardware.camera" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.CAMERA" />

Overview-iOS

SDK requirements and limitations as below:

  • Minimum iOS version: iOS 10.0
  • Additional dependent third-party libraries: None
  • Supported CPU architectures: arm64, x86_64
  • SDK package size: 6.1MB(arm64, disable bitcode)
  • Supported bitcode: NO
  • Supported languages: en, zh-Hans, id, vi, th, es, ms, hi,fil
  • Use-permissions: NSCameraUsageDescription

Compliance Explanation

Click to view the compliance explanation

Release Notes

Click to show release notes

Demo

Install IDV-Demo.apk to your phone and log in with the test account.

Migration Guides

If you are upgrading from an older version of the Liveness SDK, please refer to this document to understand the changes.

Installation

Add the dependency in your React Native project’s package.json file.

  "dependencies": {
    ...
    "liveness-plugin": "^4.0.1"
  },

For Android, you need to do:

Add the Maven repository to the settings.gradle or build.gradle file in the Android directory of your React Native project:

rootProject.allprojects {  
    repositories {
        google()
        jcenter()
        repositories {
            maven {
                url 'https://public-n3.advai.net/repository/maven-releases/'
            }
        }
    }
}

For iOS, you need to do:

  1. specify the SDK name and url in the Podfile.
pod 'AAINetwork', :http => 'https://prod-guardian-cv.oss-ap-southeast-5.aliyuncs.com/sdk/iOS-libraries/AAINetwork/AAINetwork-V1.0.4.tar.bz2', type: :tbz 

pod 'AAILivenessUI', :http => 'https://prod-guardian-cv.oss-ap-southeast-5.aliyuncs.com/sdk/iOS-liveness-detection/4.0.0/iOS-Liveness-SDK-V4.0.0.tar.bz2', type: :tbz

pod 'AAILivenessModel', :http => 'https://prod-guardian-cv.oss-ap-southeast-5.aliyuncs.com/sdk/iOS-libraries/AAILivenessModel/4.0.0/AAILivenessModel-V4.0.0.tar.bz2', type: :tbz

pod 'AAICore', :http => 'https://prod-guardian-cv.oss-ap-southeast-5.aliyuncs.com/sdk/iOS-libraries/AAICore/1.0.0/AAICore-V1.0.0.tar.bz2', type: :tbz
  1. Run pod install to install the dependencies in your project.
  2. Add camera usage description in Info.plist as bellow. Ignore this step if you have added those.
<key>NSCameraUsageDescription</key>
<string>Use the camera to detect the face movements</string>

Usage

  1. Initialization SDK.

    const SELECTED_MARKET: Market = 'Indonesia';
    await initSDKOfLicense(SELECTED_MARKET);
  2. Check license

    The license is obtained by your server calling our openAPI, you need to check license before starting the liveness detection activity.

    const license = "xxx";
    const result = await setLicenseAndCheck(license);
    if (result === 'SUCCESS') {
    	startLivenessDetection()
    }
    
    
    The returned values of checkResult:
    
    APPLICATION_ID_NOT_MATCH: The package name is not within the authorized scope, please check your package name.
    
    LICENSE_EXPIRE: The license has expired, please confirm that the user has calibrated the phone time.
    
    ERROR_LICENSE(1): The license parsing succeeded, but necessary authentication information is missing, this case generally will not occur.
    
    ERROR_LICENSE(2): The license parsing succeeded, but the internal format is incorrect, this case also generally will not occur.
    
    ERROR_LICENSE(3): It is highly likely that an incompatible SDK license is being used, such as using an IQA license for liveness detection.
    
    ERROR_LICENSE(4, 5): Parsing failed, please check if the license has issues like mismatched quotes, line breaks, etc.
  3. You can create SDK launch parameters using the method below

    import { useState, useEffect } from 'react';
    import {
      StyleSheet,
      View,
      Text,
      Button,
      ScrollView,
      SafeAreaView,
      ActivityIndicator,
      StatusBar,
      Image,
      Alert,
      Platform,
    } from 'react-native';
    
    // Import all methods and types from your plugin
    import {
      getSDKVersion,
      initSDKOfLicense,
      setLicenseAndCheck,
      startLivenessDetection,
    } from 'liveness-plugin';
    
    import type { Market } from 'liveness-plugin';
    import type { LivenessDetectionResult } from 'liveness-plugin';
    import type { LivenessParams } from 'liveness-plugin';
    
    // --- Configuration Constants ---
    // Please replace with your real License key
    const YOUR_LICENSE_KEY = '';
    // Select a market
    const SELECTED_MARKET: Market = 'Indonesia';
    
    export default function App() {
      // --- State Management ---
      const [sdkVersion, setSdkVersion] = useState<string>('');
      const [livenessResult, setLivenessResult] =
        useState<LivenessDetectionResult | null>(null);
      const [isLoading, setIsLoading] = useState<boolean>(false);
      const [statusMessage, setStatusMessage] = useState<string>('Ready.');
    
      // --- Effect Hook: Get SDK version when the component mounts ---
      useEffect(() => {
        // Define an async function to fetch the version
        const fetchVersion = async () => {
          try {
            const version = await getSDKVersion();
            setSdkVersion(version);
          } catch (error) {
            console.error('Failed to get SDK version:', error);
            setSdkVersion('Error fetching version');
          }
        };
    
        fetchVersion();
      }, []); // Empty dependency array, runs only once when the component first mounts
    
      // --- Core Business Logic: The flow executed when the button is pressed ---
      const handleStartLiveness = async () => {
        setIsLoading(true); // Start loading
        setLivenessResult(null); // Clear old results
    
        try {
          // Step 1: Initialize SDK (with License)
          setStatusMessage(`Initializing SDK for ${SELECTED_MARKET}...`);
          await initSDKOfLicense(SELECTED_MARKET);
          console.log('SDK initialized with license.');
    
          // Step 2: Set and check the License
          setStatusMessage('Setting and checking license...');
          const checkResult = await setLicenseAndCheck(YOUR_LICENSE_KEY);
          console.log('License check result:', checkResult);
    
          // Step 3: Check the license result, start liveness detection if successful
          if (checkResult === 'SUCCESS') {
            setStatusMessage(
              'License check passed. Starting liveness detection...'
            );
    
            // Step 4: Define liveness detection parameters
            // Note: All parameters are optional.
            const livenessParams: LivenessParams = {
              cameraType: 'FRONT',
              queryId: 'your_query_id', // Replace with your query ID
              ticket: 'your_ticket', // Replace with your ticket
              detectOcclusion: false, // Whether to detect occlusion
              auditImageConfig: {
                enableCollectSwitch: true, // Whether to enable the collection switch
                imageWidth: 400, // Image width
                imageQuality: 30, // Image quality
                relativeSecondsCaptureAfterCameraLaunched: 3.0, // Capture at a relative number of seconds after the camera is launched
              },
              livenessType: 'test_more', // Liveness detection type
              signatureId: '', // Signature ID, if available
              distantNearTimeout: 50000, // Timeout for distant-near detection in milliseconds
              silentTimeout: 50000, // Timeout for silent detection in milliseconds
              actionTimeout: 10000, // Timeout for actions in milliseconds
              prepareMillSeconds: 0, // Preparation time in milliseconds
              resultPictureSize: 600, // Result picture size
              maxRecordVideoSeconds: 60, // Maximum video recording time in seconds
              userId: '', // User ID, in JSON string format
              maskColor: '#000000', // Mask color
              ovalColor: '#000000', // Oval color
              ovalNormalColor: '#000000', // Normal oval color
            };
    
            // Step 5: Call liveness detection and pass a callback function to handle the result
            startLivenessDetection(livenessParams, (result) => {
              console.log('Liveness detection result received:', result);
              setStatusMessage('Liveness detection finished.');
              setLivenessResult(result); // Save the result to state for display
              setIsLoading(false); // Stop loading
            });
    
            // Note: startLivenessDetection itself is launched asynchronously.
            // The result will be returned via callback at a future point, so there's nothing more to do here.
          } else {
            // License check failed
            throw new Error(`License check failed with result: ${checkResult}`);
          }
        } catch (error: any) {
          // Catch any error in the entire process
          console.error('Liveness process failed:', error);
          setStatusMessage(`Error: ${error.message}`);
          Alert.alert(
            'Error',
            error.message || 'An unknown error occurred during the process.'
          );
          setIsLoading(false); // Stop loading
        }
      };
      // --- Render UI ---
      return (
        <SafeAreaView style={styles.safeArea}>
          <StatusBar barStyle={'dark-content'} />
          <View style={styles.container}>
            <View style={styles.header}>
              <Text style={styles.title}>Liveness Plugin Example</Text>
              <Text style={styles.versionText}>SDK Version: {sdkVersion}</Text>
            </View>
    
            <View style={styles.buttonContainer}>
              <Button
                title="Start Liveness"
                onPress={handleStartLiveness}
                disabled={isLoading} // Disable button while loading
              />
            </View>
    
            <View style={styles.statusContainer}>
              {isLoading && <ActivityIndicator style={styles.loader} />}
              <Text style={styles.statusText}>{statusMessage}</Text>
            </View>
    
            <View style={styles.resultContainer}>
              <Text style={styles.resultTitle}>Detection Result:</Text>
              <ScrollView style={styles.resultScrollView}>
                <Text style={styles.resultJson}>
                  {livenessResult
                    ? `isSuccess: ${livenessResult.isSuccess}\n` +
                      `livenessId: ${livenessResult.livenessId ?? 'N/A'}\n` +
                      `eventId: ${livenessResult.eventId ?? 'N/A'}\n` +
                      `code: ${livenessResult.code ?? 'N/A'}\n` +
                      `videoFilePath: ${livenessResult.videoFilePath ?? 'N/A'}\n` +
                      `message: ${livenessResult.message ?? 'N/A'}\n` +
                      `transactionId: ${livenessResult.transactionId ?? 'N/A'}\n` +
                      `isPay: ${livenessResult.isPay ?? 'N/A'}`
                    : 'No result yet.'}
                </Text>
                {livenessResult && livenessResult.base64Image && (
                  <View style={styles.imageContainer}>
                    <Text style={styles.imageLabel}>Liveness Image:</Text>
                    <Image
                      style={styles.livenessImage}
                      source={{
                        uri: `data:image/jpeg;base64,${livenessResult.base64Image}`,
                      }}
                    />
                  </View>
                )}
              </ScrollView>
            </View>
          </View>
        </SafeAreaView>
      );
    }
    
    const styles = StyleSheet.create({
      safeArea: {
        flex: 1,
        backgroundColor: '#F5F5F5',
      },
      container: {
        flex: 1,
        padding: 16,
      },
      header: {
        alignItems: 'center',
        marginBottom: 24,
      },
      title: {
        fontSize: 24,
        fontWeight: 'bold',
      },
      versionText: {
        fontSize: 14,
        color: '#f00',
        marginTop: 4,
      },
      buttonContainer: {
        marginBottom: 20,
      },
      statusContainer: {
        flexDirection: 'row',
        alignItems: 'center',
        justifyContent: 'center',
        marginBottom: 20,
        minHeight: 30,
      },
      loader: {
        marginRight: 10,
      },
      statusText: {
        fontSize: 16,
        color: '#333',
      },
      resultContainer: {
        flex: 1,
        borderWidth: 1,
        borderColor: '#DDD',
        borderRadius: 8,
        padding: 10,
        backgroundColor: '#FFFFFF',
      },
      resultTitle: {
        fontSize: 18,
        fontWeight: '600',
        marginBottom: 8,
        borderBottomWidth: 1,
        borderBottomColor: '#EEE',
        paddingBottom: 8,
      },
      resultScrollView: {
        flex: 1,
      },
      resultJson: {
        fontFamily: Platform.OS === 'ios' ? 'Menlo' : 'monospace',
        fontSize: 12,
        color: '#000',
      },
      imageContainer: {
        padding: 10,
        borderBottomWidth: 1,
        borderBottomColor: '#EEE',
        alignItems: 'center',
      },
      imageLabel: {
        fontSize: 14,
        fontWeight: '500',
        alignSelf: 'flex-start',
        marginBottom: 8,
      },
      livenessImage: {
        width: 200,
        height: 200,
        resizeMode: 'contain',
        borderWidth: 1,
        borderColor: '#DDD',
      },
    });
    

Error Code

See Error Code