React Native
Nuestro SDK de Workflows para React Native permite una experiencia rápida y fluida para tus usuarios, aprovechando las capacidades nativas de los dispositivos. Este SDK se instala como un paquete npm y te permite ejecutar workflows, obtener información detallada del proceso y recibir eventos en tiempo real durante su ejecución.
Requisitos
Para poder utilizar nuestro SDK de React Native, debes cumplir con los siguientes requisitos:
- React Native 0.70+
- Node.js 18+
- Contar con un Workflow creado a través de nuestra API de Workflows.
- Acceso al registro privado de npm (se te proporcionará un archivo
.npmrc).
Instalación
Configuración del archivo .npmrc
En la raíz del proyecto React Native, agrega el archivo .npmrc para autorizar la instalación del paquete. Este archivo te será proporcionado por tu administrador de cuenta.
Instalación del paquete
Ejecuta el siguiente comando en la raíz del proyecto:
npm install @rem-tools/workflows-react-native@0.5.0
Nota: Verifica la versión actual en el registro de npm.
Configuración para Expo
Si utilizas Expo, puedes omitir la configuración nativa usando el comando:
npx expo prebuild
En tu archivo app.json o app.config.js, agrega la configuración del plugin:
{
"expo": {
"plugins": [
[
"@rem-tools/workflows-react-native",
{
"jsonKeyPath": "/ruta/a/tu/archivo-service-account.json",
"cameraDescription": "Se requiere acceso a la cámara para verificación de identidad",
"microphoneDescription": "Se requiere acceso al micrófono para grabar audio",
"locationDescription": "Se requiere acceso a la ubicación por motivos de seguridad"
}
]
]
}
}
Opciones de Configuración del Plugin
| Parámetro | Requerido | Descripción |
|---|---|---|
jsonKeyPath | Sí | Ruta absoluta o relativa al archivo JSON de la cuenta de servicio de Google Cloud. Requerido para Android para autenticarse con el repositorio Maven privado. |
cameraDescription | No | Descripción personalizada para el permiso de cámara (iOS). |
microphoneDescription | No | Descripción personalizada para el permiso de micrófono (iOS). |
locationDescription | No | Descripción personalizada para el permiso de ubicación (iOS). |
Ejecutar la aplicación
Para Android (dispositivo físico):
Asegúrate de que el proyecto Android haya completado el build de Gradle:
npm run android --device
Para iOS (dispositivo físico):
Asegúrate de haber ejecutado pod install en el directorio ios:
npx expo run:ios --device
Configuración Específica para Android
Requisitos de SDK de Android
- Android API 24 - 36
- Android Studio Hedgehog+
Configuración de Repositorios
En el archivo android/build.gradle, declara de dónde se obtendrán las dependencias:
allprojects {
repositories {
maven {
url "https://us-central1-maven.pkg.dev/gendra-services/workflows-mobile"
credentials {
username = "_json_key"
password = file("tu_archivo_credenciales.json").text
}
}
}
}
El archivo de service account (JSON) es proporcionado por REM. Contacta a tu administrador de cuenta para obtenerlo.
Configuración de Release Build
En el archivo android/app/build.gradle, agrega a nivel de android los siguientes ajustes que permiten que Workflows pueda acceder a modelos, assets, y evitar errores en compilación de release:
android {
// ...
buildTypes {
release {
shrinkResources false
}
}
aaptOptions {
noCompress "tflite", "task"
}
sourceSets {
main {
assets.srcDirs += 'src/main/assets'
}
}
}
Configuración del AndroidManifest
Agrega esta línea para resolver conflictos del AndroidManifest cuando una librería (como Workflows) define los mismos atributos:
<application
...
tools:replace="android:theme, android:usesCleartextTraffic">
...
</application>
Reglas para ProGuard
Agrega las siguientes reglas al archivo proguard-rules.pro. Son necesarias para evitar la minificación de clases usadas por Workflows:
# Keep the io.flutter.plugin.** package provided by Flutter
-dontwarn io.flutter.plugin.**
# The android.** package is provided by the OS at runtime.
-dontwarn android.**
# Rules for the Workflows Flutter
-keep class tools.rem.workflows_flutter_plugin.** { *; }
-keep class com.digitalpersona.** { *; }
# Keep the mediapipe classes
-keep public class com.google.mediapipe.** { *; }
-keep public class com.google.mediapipe.framework.Graph.** { *; }
-keep public class com.google.common.** { *; }
-keep public interface com.google.common.** { *; }
-keep class * extends com.google.common.flogger.** {*;}
-keep class com.google.protobuf.** { *; }
-keepclassmembers class * extends com.google.protobuf.GeneratedMessageLite.** { *; }
-dontwarn com.google.mediapipe.proto.CalculatorProfileProto$CalculatorProfile
-dontwarn com.google.mediapipe.proto.GraphTemplateProto$CalculatorGraphTemplate
Recursos Personalizados
Para usar recursos personalizados, agrega los assets en la carpeta res según su tipo:
| Carpeta | Tipo de Recurso | Formatos |
|---|---|---|
drawable | Imágenes | .png, .jpeg |
font | Tipografías | .ttf, .otf |
raw | Imágenes vectoriales | .svg |
xml | Textos localizados | .xml |
Configuración Específica para iOS
Requisitos de iOS
- iOS 15.1 - 26.0
- Xcode 14.0+
Configuración del Podfile
En el archivo ios/Podfile, establece la versión mínima en 15.1 y agrega el pod de WorkflowController:
platform :ios, '15.1'
target 'TuApp' do
# Otras configuraciones y pods
pod 'WorkflowController', :podspec => 'https://storage.googleapis.com/workflows-mobile-sdk-artifacts/WorkflowiOS/WorkflowController/WorkflowsController.podspec'
end
Nota: El target
WorkflowsReactNativees generado automáticamente por CocoaPods al instalar este paquete. No es necesario modificar este nombre.
Agrega el siguiente bloque post_install en tu Podfile:
post_install do |installer|
system("mv Pods/WorkflowController/MediaPipeTasks*.xcframework Pods/")
installer.pods_project.targets.each do |target|
if target.name == 'WorkflowsReactNative'
puts "[WorkflowSDK] MATCHED Pod Target: WorkflowsReactNative"
group = target.project.main_group.find_subpath("Pods/WorkflowController", true)
%w(MediaPipeTasksVision.xcframework MediaPipeTasksCommon.xcframework).each do |fw|
file_path = File.join(Pod::Config.instance.installation_root, "Pods", "WorkflowController", fw)
puts "[WorkflowSDK] Adding #{fw} to #{target.name}"
ref = group.new_file(file_path)
ref.source_tree = 'SOURCE_ROOT'
target.frameworks_build_phase.add_file_reference(ref, true)
build_files = target.frameworks_build_phase.files.select { |f| f.file_ref == ref }
build_files.each { |bf| bf.settings = { 'ATTRIBUTES' => ['Required'] } }
end
end
target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.1'
config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = "arm64"
config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO'
end
end
installer.pods_project.save
end
Instalación de Dependencias
Desde la carpeta ios de tu proyecto, ejecuta:
pod install
Permisos en Info.plist
Agrega los permisos necesarios al archivo Info.plist o en el target properties seleccionando la pestaña Info:
<key>NSCameraUsageDescription</key>
<string>3D Liveness Detection by FaceTec.</string>
<key>NSMicrophoneUsageDescription</key>
<string>We need access to your microphone to record audio.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>The example App requires access to the devices location.</string>
Recursos Personalizados
Para usar recursos personalizados, agrégalos directamente a la carpeta del proyecto:
| Tipo de Recurso | Formato | Ubicación |
|---|---|---|
| Textos | .strings | Carpeta del proyecto |
| Imágenes SVG | .svg | Carpeta del proyecto |
| Tipografías | .otf, .ttf | Carpeta del proyecto |
| Imágenes PNG/JPG | .png, .jpg | Asset Catalog |
Uso del SDK
Importación
Importa los componentes necesarios del SDK:
import type { WorkflowCustomization } from '@rem-tools/workflows-react-native';
import {
closeWorkflow,
useWorkflow,
useWorkflowEvents,
verifyWorkflowsLicense
} from '@rem-tools/workflows-react-native';
Verificación de Licencia
Antes de iniciar un workflow, debes verificar la licencia del SDK:
verifyWorkflowsLicense({
cert: "tu_certificado", // Base64-encoded PFX
passphrase: "tu_passphrase" // Base64-PFX passphrase
});
Las credenciales de licencia (cert y passphrase) serán compartidas de forma privada por tu administrador de cuenta.
Inicialización del Workflow
Utiliza el hook useWorkflow para configurar y lanzar un workflow:
const launchWorkflow = useWorkflow({
baseUrl: 'https://api.rem.tools', // Your Workflows backend URL
apiKey: 'tu_api_key', // Your API key
workflowId: 'tu_workflow_id', // Workflow ID to execute
customization: getWorkflowCustomization() // null = default UI, or customize
});
Manejo de Eventos
Utiliza el hook useWorkflowEvents para escuchar los eventos del workflow:
const { workflowStart, workflowEvent, workflowStep } = useWorkflowEvents();
// Evento de inicio del workflow
useEffect(() => {
if (workflowStart) {
console.log("Workflow started success:", workflowStart.success);
if (workflowStart.error !== undefined) {
console.log("Workflow Id Error:", workflowStart.error);
}
}
}, [workflowStart]);
// Eventos del workflow
useEffect(() => {
if (workflowEvent) {
console.log("Workflow UUID:", workflowEvent.uuid, "Status:", workflowEvent.status);
if (workflowEvent.error) {
console.log("Workflow Error:", workflowEvent.error);
}
}
}, [workflowEvent]);
// Eventos de pasos
useEffect(() => {
if (workflowStep) {
console.log("Step:", workflowStep.step, "Status:", workflowStep.status);
const error = workflowStep.error;
if (error && (workflowStep.status === "error" || workflowStep.status === "failed")) {
console.log("Error - fatal:", error.fatal,
"errorId:", error.details?.errorId,
"reason:", error.details?.reason);
const extra = error.extra;
if (extra) {
if (extra.idScanStatus != null || extra.faceScanStatus != null) {
console.log("idScanStatus:", extra.idScanStatus,
"faceScanStatus:", extra.faceScanStatus);
}
if (extra.permission != null) {
console.log("Permission denied:", extra.permission);
// Cerrar la actividad/vista de workflows
closeWorkflow();
}
if (extra.secure != null) {
console.log("Is device secure:", extra.secure);
}
if (extra.isLicenseValid != null) {
console.log("Is license valid:", extra.isLicenseValid);
}
}
}
}
}, [workflowStep]);
Lanzar el Workflow
Para lanzar la pantalla de workflows, simplemente llama al hook:
<Button title="Iniciar Workflow" onPress={launchWorkflow} />
Cerrar el Workflow
Workflows se abre en una nueva activity/controller. Para cerrar, usa la función closeWorkflow():
closeWorkflow();
Personalización del SDK
Puedes personalizar la apariencia del SDK mediante la función de configuración:
import { Platform } from 'react-native';
function getWorkflowCustomization(): WorkflowCustomization {
return {
theme: {
primaryColor: '#61DBFB',
secondaryColor: '#20232A',
buttonPrimaryColor: '#61DBFB',
buttonSecondaryColor: '#20232A',
buttonTextPrimaryColor: '#FF6F61',
buttonTextSecondaryColor: '#F9A826',
backgroundColor: '#F5F5F5',
facetecOverlayColor: '#F5F5F5',
showHeaderLogo: true
},
textLocalization: {
localization: Platform.OS === 'android'
? 'localizable.xml'
: 'Localizable.strings'
},
textFont: {
headerFont: Platform.OS === 'android'
? 'montserrat_black.ttf'
: 'Montserrat-Black.ttf',
bodyFont: Platform.OS === 'android'
? 'montserrat_regular.ttf'
: 'Montserrat-Regular.ttf',
buttonFont: Platform.OS === 'android'
? 'playwrite_ar_regular.ttf'
: 'PlaywriteAR-Regular.ttf'
},
images: {
logo: 'logo.png',
cameraPermissions: 'cam.png',
uploadFaceScan: 'upload.png',
uploadFaceSuccess: 'exito_success.png',
uploadFaceError: 'error_image.png',
photoMathId: 'id.png',
cameraActiveTorch: 'on_flash.png',
cameraInactiveTorch: 'off_flash.png',
fingerPrint: 'fing.png',
successPage: 'success.svg',
errorPage: 'error.svg',
unavailablePage: 'error.svg',
locationPage: 'ubicacion.svg',
biometricSignPage: 'face_match.svg',
enrollFingerprintPage: 'face_match.svg',
enrollBasicPage: 'face_match.svg',
enrollFullPage: 'face_match.svg',
authPage: 'face_match.svg',
livenessPage: 'face_match.svg',
faceEnrollment3dPage: 'face_match.svg'
}
};
}
Ejemplo Completo
import React, { useEffect } from 'react';
import { View, Button, Platform } from 'react-native';
import type { WorkflowCustomization } from '@rem-tools/workflows-react-native';
import {
closeWorkflow,
useWorkflow,
useWorkflowEvents,
verifyWorkflowsLicense
} from '@rem-tools/workflows-react-native';
function getWorkflowCustomization(): WorkflowCustomization {
return {
theme: {
primaryColor: '#61DBFB',
secondaryColor: '#20232A',
buttonPrimaryColor: '#61DBFB',
buttonSecondaryColor: '#20232A',
buttonTextPrimaryColor: '#FFFFFF',
buttonTextSecondaryColor: '#FFFFFF',
backgroundColor: '#F5F5F5',
facetecOverlayColor: '#000000',
showHeaderLogo: true
},
textLocalization: {
localization: Platform.OS === 'android'
? 'localizable.xml'
: 'Localizable.strings'
},
textFont: {
headerFont: Platform.OS === 'android'
? 'montserrat_black.ttf'
: 'Montserrat-Black.ttf',
bodyFont: Platform.OS === 'android'
? 'montserrat_regular.ttf'
: 'Montserrat-Regular.ttf',
buttonFont: Platform.OS === 'android'
? 'montserrat_bold.ttf'
: 'Montserrat-Bold.ttf'
},
images: {
logo: 'logo.png',
cameraPermissions: 'camera.png',
uploadFaceScan: 'upload.png',
uploadFaceSuccess: 'success.png',
uploadFaceError: 'error.png',
photoMathId: 'id.png',
cameraActiveTorch: 'flash_on.png',
cameraInactiveTorch: 'flash_off.png',
fingerPrint: 'fingerprint.png',
successPage: 'success.svg',
errorPage: 'error.svg',
unavailablePage: 'unavailable.svg',
locationPage: 'location.svg',
biometricSignPage: 'biometric.svg',
enrollFingerprintPage: 'fingerprint.svg',
enrollBasicPage: 'enroll.svg',
enrollFullPage: 'enroll_full.svg',
authPage: 'auth.svg',
livenessPage: 'liveness.svg',
faceEnrollment3dPage: 'face_3d.svg'
}
};
}
export default function WorkflowScreen() {
// Verificar licencia al iniciar
useEffect(() => {
verifyWorkflowsLicense({
cert: "tu_certificado",
passphrase: "tu_passphrase"
});
}, []);
// Configurar el workflow
const launchWorkflow = useWorkflow({
baseUrl: 'https://api.rem.tools',
apiKey: 'tu_api_key',
workflowId: 'tu_workflow_id',
customization: getWorkflowCustomization()
});
// Escuchar eventos
const { workflowStart, workflowEvent, workflowStep } = useWorkflowEvents();
useEffect(() => {
if (workflowStart) {
console.log("Workflow started:", workflowStart.success);
}
}, [workflowStart]);
useEffect(() => {
if (workflowEvent) {
console.log("Workflow status:", workflowEvent.status);
}
}, [workflowEvent]);
useEffect(() => {
if (workflowStep) {
console.log("Step status:", workflowStep.status);
}
}, [workflowStep]);
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Button title="Iniciar Workflow" onPress={launchWorkflow} />
</View>
);
}
FAQ
¿Cómo obtengo el workflowId?
El workflowId se obtiene al crear un Workflow mediante nuestra API de Workflows. La respuesta de la API te proporcionará este identificador.
¿Cómo consigo el apiKey y el baseUrl?
El apiKey es proporcionado de forma privada, y el baseUrl suele ser:
https://api.rem.toolspara producción.https://api.test.rem.toolspara pruebas.
¿Cómo obtengo las credenciales de licencia?
Las credenciales de licencia (cert y passphrase) para la función verifyWorkflowsLicense serán compartidas de forma privada por tu administrador de cuenta.
¿Cómo cierro el workflow programáticamente?
Utiliza la función closeWorkflow() que se importa del SDK. Esto cerrará la activity/controller del workflow.
Si tienes alguna duda o problema con la integración del SDK, por favor contacta a soporte.



