# FaceAISDK React Native: on-device face verification and liveness

> FaceRecognition_ReactNative is a React Native demo and integration guide for an on-device face SDK that does enrollment, verification and liveness detection without a server. The interesting part is what the README documents about the current release, including a TensorFlowLite ABI problem that crashes model initialization and a workaround you have to paste into your own Podfile.

**FaceAISDK/FaceRecognition_ReactNative** — ReactNative on-device face verification, liveness detection, and face enrollment for Android and iOS.

- Repository: https://github.com/FaceAISDK/FaceRecognition_ReactNative
- Website: https://faceaisdk.github.io/index
- Stars: 563 · Forks: 112
- Language: TypeScript
- License: not declared
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/faceaisdk-facerecognition-reactnative

## What the demo is, and where the plugin actually lives

This repository is a consumer demo, and the README says so in its first technical paragraph. The main branch contains the npm consumer demo, while the plugin source and release tools are maintained on the dev branch. That split matters for anyone who came looking for the plugin source, and it also explains the release tags: v1.7.2 in October 2026 is described as an Android SDK update, and two of the three recent releases are dated versions rather than semver, which is consistent with the plugin being published separately from this demo.

The package being consumed is `@faceaisdk/react-native-face-sdk` on npm, and the demo's own package.json records it as a dependency at `^1.7.4`. The scope of the SDK is stated plainly in the README's summary line: offline face enrollment, verification, and liveness detection on iOS and Android. The word that matters there is offline. Every call in the API table is a local operation on a face feature held on the device, which is the reason a project would choose this over a server-side face service.

The documentation site is faceaisdk.github.io/index, the repository topics name face detection, face recognition, face verification and liveness detection, and the primary language of the repository is TypeScript. The last push to the repository was on 2026-10-03.

The demo also has a Chinese README at README.zh-CN.md, and the top level of the repository is a standard React Native 0.84 project: App.tsx as the single example file, index.js, app.json, babel.config.js, metro.config.js, jest.config.js, tsconfig.json, a Gemfile, android/ and ios/ directories, a __tests__/ directory and a scripts/ directory.

## The API surface, and the error code that catches everyone

The demo exposes seven calls, and each one returns a `Promise<FaceResult>`. The options object for the two verification calls is the part worth reading closely.

```ts
const faceID = 'demo-user';
const options = {
  livenessType: 1 as const,
  motionTypes: '1,2,3,4,5',
  timeout: 7,
  steps: 2,
  allowMultiFaces: true,
};
```

The table in the README maps the menu to the calls: enrollment with the camera is `addFaceBySDKCamera(faceID, {mode: 1, showConfirm: true})`, verification combined with liveness is `faceVerify(faceID, options)`, liveness on its own is `livenessVerify(options)`, querying a stored feature is `getFaceFeature(faceID)`, inserting one is `insertFaceFeature(faceID, feature)`, enrolling from a Base64 image is `addFaceByImage(faceID, base64Image)`, and deletion is `deleteFaceFeature(faceID)`.

The behavioural rules are where the real documentation is. A face has to be enrolled before verification or lookup, though standalone liveness needs no enrollment. And the failure the README highlights is the one you will hit first: on iOS, calling the plugin's `faceVerify` without an enrolled feature returns `code: 6`, regardless of Debug or Release mode. The demo handles it by checking the `demo-user` feature before verification and showing enrollment instructions when it is missing.

Two option semantics are called out because they are easy to get wrong. `motionTypes` is a string of comma-separated motion IDs, and `timeout` and `steps` control the timeout and the action count. `allowMultiFaces` is forwarded to the Android SDK in 1.7.4, while the iOS bridge accepts the parameter but does not forward it to Core, so the same code behaves differently on the two platforms. And `faceVerify` accepts a `threshold` whose default is `0.83`, which is a similarity threshold, not a liveness threshold.

## Checking the result code instead of trusting the promise

The result type is flat, and every field of it is a plain value.

```ts
interface FaceResult {
  code: number;
  message: string;
  faceID: string;
  similarity: number;
  liveness: number;
  faceFeature: string;
  faceBase64: string;
}
```

The instruction that comes with it is the one to design around: check `code` and `message`, because an SDK business failure does not necessarily reject the promise. In other words, a resolved promise in this SDK is not a success. A caller that only uses try and catch will treat a failed verification as a successful call, and the demo guards against that by displaying feature and image lengths rather than the large raw values they contain.

The two numeric fields have different meanings, and the README is careful to separate them. `similarity` is the match score for the verification, and `liveness` is the liveness score, with the threshold parameter belonging to the former. The `faceFeature` and `faceBase64` fields are strings carrying the raw feature and image payloads, and the demo deliberately does not print them.

Two constants in App.tsx are empty by default: `DEMO_FACE_FEATURE` and `DEMO_BASE64_IMAGE`. Until they are configured, the menu items that use them show a reminder without calling the SDK, and the README warns that inserting a feature overwrites the data for the same face ID, which is the behaviour to keep in mind before pointing the demo at real enrolment data.

## Running it: Metro on a fixed port, and a physical device

The requirements are specific and unforgiving. Node.js 22.11 or newer, React Native 0.84.0, the community CLI at 20.2.0, and Face SDK 1.7.4 with the Android SDK at 2026.09.29 and the iOS Core at 2026.09.22. Xcode and CocoaPods are needed for iOS, and the Android SDK with compile SDK 34 and JDK 17 for Android. The device requirement is a physical one: iOS 15.5 or newer, or Android API 24 and up, and simulators are not supported.

Dependencies install the usual way.

```bash
npm install
```

For Android and iOS Debug you run `npm start` to keep Metro on port 8765 and use a second terminal for the platform command, while iOS Release builds bundle the JavaScript and do not need Metro. The Android build is a single command with USB debugging enabled on the device.

```bash
npm run android
```

The iOS route has more steps. Pods first, from the ios directory.

```bash
cd ios
pod install
cd ..
```

Then you open `ios/FaceAISDK_RN.xcworkspace` in Xcode, set your Development Team under Signing & Capabilities, connect an iPhone and run the platform command.

```bash
npm run ios
```

Two details there are easy to lose. The shared scheme uses Release for Run, so Debug on a physical device needs the mode flag while `npm start` keeps running, and the iPhone has to reach Metro on port 8765. If CLI installation fails with `devicectl`, the README says to select the device in Xcode and use Product > Run instead. For iOS 16 and earlier there is a separate path that uses `ios-deploy`, which builds a Release app and installs it, and its output is brief by design, with the full log written to `ios/build/ios-legacy.log` and printed automatically if the build or installation fails.

## The TensorFlowLite ABI problem, and the Podfile patch it needs

This is the part of the documentation that matters most for anyone integrating the SDK, and it is a version-specific defect with a documented workaround.

The README explains that SDK 1.7.4 automatically supplies the TensorFlowLite modulemap, and that version 1.7.4 omits a TensorFlowLiteSwift ABI setting required by the prebuilt Core, which causes model initialization to crash. The demo's Podfile carries a scoped workaround: enable distribution mode and skip emitted-interface verification for TensorFlowLiteSwift in Debug and Release, while the App target stays set to No. The dev branch contains the automatic fix, pending a new npm release.

The same problem hits an integrating project, and the README gives the steps. Set the App deployment target and `platform :ios` in your Podfile to 15.5 or later. In Xcode, select the App target, then Build Settings, then Build Libraries for Distribution, and set it explicitly to No for both Debug and Release, because Core 2026.09.22 exports YES into the host xcconfig and inheriting it can make CocoaPods enable distribution mode for unrelated Pods and fail Swift interface verification. And the camera usage text has to be in your Info.plist.

```xml
   <key>NSCameraUsageDescription</key>
   <string>Camera access is required for face recognition and liveness detection.</string>
```

For the ABI issue itself, with npm version 1.7.4 you add a block after `react_native_post_install` in your existing `post_install` callback, and the block is Ruby that walks the Pods targets and adjusts the TensorFlowLiteSwift build settings.

```ruby
flag = '-no-verify-emitted-module-interface'
installer.pods_project.targets.each do |target|
  next unless target.name == 'TensorFlowLiteSwift'
```

The README is explicit that the block is scoped to the one target and that the App target must remain set to No, which is the asymmetry that makes the workaround easy to get wrong. A developer who applies the fix globally and sets distribution mode on the App target will reintroduce the CocoaPods failure the same setting was meant to avoid.

## The demo's own scripts, and where the SDK boundary sits

The package.json scripts show how much of the tooling is React Native default and how much is specific to this project. The android and ios scripts both pin the Metro port to 8765, and the Android one passes an init script for Gradle dependency mirrors.

```json
  "scripts": {
    "android": "react-native run-android --port 8765 --extra-params=\"--init-script gradle/dependency-mirrors.init.gradle\"",
    "ios": "react-native run-ios --device --port 8765",
    "ios:legacy": "sh scripts/ios-legacy.sh",
    "lint": "eslint App.tsx __tests__",
    "start": "react-native start --port 8765",
    "test": "jest --runInBand",
    "typecheck": "tsc --noEmit"
  },
```

The three checks a contributor can run are lint over App.tsx and the tests, Jest in band, and a TypeScript check with no emit. The dependencies are React 19.2.3 and React Native 0.84.0, and the engines field requires Node 22.11 or newer.

Where the SDK boundary sits is worth being explicit about. The native face model, the feature storage and the liveness logic live in the `@faceaisdk/react-native-face-sdk` package, not in this repository. What this repository contributes is a working example of how to call it, the Podfile patch that makes version 1.7.4 build, and the scripts for the legacy install path. Anyone adopting the SDK copies the integration steps, not the model.

## Where this approach is the wrong one

Three cases point elsewhere. The first is a team that needs the verification decision to be authoritative on a server. Everything here is on-device by design, and the face feature is stored and compared locally. That is the right architecture when the privacy requirement says the biometric template must not leave the phone, and the wrong one when a check has to be consistent across a fleet of devices or revocable centrally, because a local template cannot be revoked the way a server-side record can.

The second is a team that cannot patch a Podfile. The 1.7.4 ABI defect means the integration is not a matter of adding a dependency. You have to edit your Podfile, walk the Pods targets in Ruby, and get one build setting right per configuration, and you have to remember not to set it on the App target. Until the fix ships in an npm release, that work belongs to every project that integrates the SDK. The dev branch holds the automatic fix, so the maintenance cost is real but bounded, and it disappears on the next release.

The third is a project developed on simulators. The README states plainly that simulators are not supported, so a team whose whole iOS loop is the simulator cannot use the demo as it is documented.

The alternative worth weighing is a server-side face verification service reached over the network, and the difference in approach is where the biometric template lives and who decides the match. This SDK keeps the template and the model on the device, so a check works with no connectivity and no image leaves the phone, at the cost of a per-device enrolment and a match decision you cannot centrally audit or revoke. A server-side service centralises both, and pays for it with a network round trip, a template database to protect, and a privacy story that is considerably harder to defend.

## Conclusion

FaceAISDK React Native fits a mobile team that needs face verification and liveness detection to run on the device, keeps the biometric data off a server, and can afford to maintain a Podfile patch until the next npm release lands. It does not fit a team that wants a turnkey drop-in, because the README instructs you to edit your Podfile and set build settings by hand for SDK 1.7.4, and it does not fit a simulator-based development setup, since simulators are not supported. Before you adopt it, read the ABI section carefully and check whether the dev branch version of the fix has shipped as an npm release, and confirm the licence, since the repository records none.

## FAQ

### What is the FaceRecognition_ReactNative repository?

It is a React Native consumer demo for the `@faceaisdk/react-native-face-sdk` package, demonstrating offline face enrollment, verification and liveness detection on iOS and Android. The main branch holds the demo, while the plugin source and release tools are maintained on the dev branch.

### What are the requirements for running the FaceAISDK React Native demo?

Node.js 22.11 or newer, React Native 0.84.0, the community CLI 20.2.0 and Face SDK 1.7.4, plus Xcode and CocoaPods for iOS or the Android SDK with compile SDK 34 and JDK 17 for Android. A physical device is required, iOS 15.5+ or Android API 24+, since simulators are not supported.

### Why does faceVerify fail with code 6 on iOS?

That code is returned when the plugin's faceVerify is called without an enrolled feature, in both Debug and Release mode. The demo checks the demo-user feature before verification and shows enrollment instructions when it is missing, so you have to enroll a face with the camera first.

### Do I need to change my Podfile to use Face SDK 1.7.4?

Yes. Version 1.7.4 omits a TensorFlowLiteSwift ABI setting required by the prebuilt Core, causing model initialization to crash, so you add a block after react_native_post_install that enables distribution mode and skips emitted-interface verification for TensorFlowLiteSwift, while keeping the App target's Build Libraries for Distribution set to No.

### How do I run the demo on an iPhone running iOS 16 or earlier?

Use the legacy path instead of devicectl: connect one iPhone and enter its passcode, then trust the computer, then run npm run ios:legacy from the project root. It builds a Release app and installs it, the full log goes to ios/build/ios-legacy.log, and Metro is not needed.

## Sources

- [FaceAISDK/FaceRecognition_ReactNative on GitHub](https://github.com/FaceAISDK/FaceRecognition_ReactNative)
- [Issues](https://github.com/FaceAISDK/FaceRecognition_ReactNative/issues)
- [Project website](https://faceaisdk.github.io/index)
- [README](https://github.com/FaceAISDK/FaceRecognition_ReactNative/blob/main/README.md)
- [Releases](https://github.com/FaceAISDK/FaceRecognition_ReactNative/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/faceaisdk-facerecognition-reactnative
