Skip to main content
React Native capture

Open the capture from a React Native app with a native module.

Markdown

Your back-end creates the process in Human Plugin with a Journey flow and a callbackUri that uses your app's URL scheme. A native module opens the link from the response in ASWebAuthenticationSession on iOS and in Custom Tabs on Android. When the capture ends, the module resolves its promise and the app takes over.

Keep the Human Plugin id from the create response. Your back-end reads the outcome with it.

Prerequisites​

  • React Native 0.71 or later
  • Xcode 14 or later for iOS
  • Android Studio for Android
  • A URL scheme unique to your app, such as myapp

1. Create the process with your scheme​

Your back-end sends the scheme as callbackUri:

{
"flow": "humantoken",
"clientId": "client-a",
"correlationId": "order-1001",
"credential": { "type": "DUI_TYPE_NG_BVN", "value": "<document number>" },
"callbackUri": "myapp://callback"
}

and returns id and link to the app.

2. Create the TypeScript bridge​

// src/modules/CaptureModule.ts
import { NativeModules } from 'react-native';

const { CaptureModule } = NativeModules;

if (!CaptureModule) {
throw new Error('CaptureModule not found. Make sure the native module is linked correctly.');
}

export const Capture = {
openSession: (url: string): Promise<string> => CaptureModule.openSession(url),
};

3. Implement the iOS module​

// ios/CaptureModule.m
#import <React/RCTBridgeModule.h>

@interface RCT_EXTERN_MODULE(CaptureModule, NSObject)
RCT_EXTERN_METHOD(openSession:(NSString *)url
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject)
@end

4. Implement the Android module​

Add the dependency to android/app/build.gradle:

implementation("androidx.browser:browser:1.5.0")
// android/app/src/main/java/<your.package>/CaptureModule.kt
package com.yourapp

import android.net.Uri
import androidx.browser.customtabs.CustomTabsIntent
import com.facebook.react.bridge.*

class CaptureModule(private val reactContext: ReactApplicationContext) :
ReactContextBaseJavaModule(reactContext) {

private var pendingPromise: Promise? = null

override fun getName() = "CaptureModule"

@ReactMethod
fun openSession(url: String, promise: Promise) {
pendingPromise = promise
currentActivity?.runOnUiThread {
CustomTabsIntent.Builder().build()
.launchUrl(currentActivity!!, Uri.parse(url))
} ?: promise.reject("NO_ACTIVITY", "No current activity")
}

fun handleRedirectUrl(uri: Uri) {
if (uri.scheme == "myapp") {
pendingPromise?.resolve(uri.toString())
pendingPromise = null
}
}
}
// CapturePackage.kt
package com.yourapp

import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager

class CapturePackage : ReactPackage {
override fun createNativeModules(ctx: ReactApplicationContext): List<NativeModule> =
listOf(CaptureModule(ctx))
override fun createViewManagers(ctx: ReactApplicationContext): List<ViewManager<*, *>> =
emptyList()
}

Register it in MainApplication.kt:

override fun getPackages(): List<ReactPackage> =
PackageList(this).packages.apply {
add(CapturePackage())
}

Handle the return in MainActivity.kt:

override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
intent.data?.let { uri ->
if (uri.scheme == "myapp") {
val module = reactInstanceManager
.currentReactContext
?.getNativeModule(CaptureModule::class.java)
module?.handleRedirectUrl(uri)
}
}
}

5. Register your scheme on Android​

In AndroidManifest.xml:

<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-permission android:name="android.permission.CAMERA" />

<activity android:name=".MainActivity" ...>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" android:host="callback" />
</intent-filter>
</activity>

6. Start the capture​

import { Capture } from './modules/CaptureModule';

try {
await Capture.openSession(link);
// The capture ended. Ask your back-end to read the outcome
// with GET /v1/processes/{id}, using the Human Plugin id.
} catch {
// The person closed the session.
}

Summary​

FilePurpose
src/modules/CaptureModule.tsTypeScript bridge
ios/CaptureModule.mObjective-C declaration
ios/CaptureModule.swiftiOS: ASWebAuthenticationSession
android/.../CaptureModule.ktAndroid: Custom Tabs
android/.../CapturePackage.ktPackage registration
android/.../MainApplication.ktAdds the package
android/.../MainActivity.ktHandles the return
AndroidManifest.xmlIntent filter