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
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:
- 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
- Run
pod install
to install the dependencies in your project. - 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
-
Initialization SDK.
const SELECTED_MARKET: Market = 'Indonesia'; await initSDKOfLicense(SELECTED_MARKET);
-
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.
-
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
Updated 5 days ago