Skip to main content

Nativo

Nuestro SDK Nativo de Workflows para Android e iOS permite una experiencia más eficiente y segura para el usuario final. El SDK de Workflows de Nativo permite a los desarrolladores de aplicaciones móviles integrar la funcionalidad de Workflows en sus aplicaciones nativas de Android e iOS.

iOS​

Requisitos​

  • iOS 13.0 - 26.0
  • Xcode 14.0+

La integración del SDK de Workflows en tu aplicación iOS es un proceso sencillo que consta de los siguientes pasos:

Configuración​

Instalación de CocoaPods​

Para integrar el SDK de Workflows, primero asegúrate de contar con un archivo Podfile en la raíz de tu proyecto iOS.

Si tu proyecto no cuenta con un Podfile, abre una terminal, navega a la carpeta donde se encuentra el archivo .xcodeproj y ejecuta:

pod init

Este comando creará el archivo Podfile en tu proyecto.

Después, configura el Podfile para soportar la versión mínima de iOS requerida y agrega las dependencias del SDK:

platform :ios, '13.0'

target 'TuApp' do
use_frameworks!

pod 'WorkflowControllerDebug',
:podspec => 'https://storage.googleapis.com/workflows-mobile-sdk-artifacts/WorkflowiOS/WorkflowController/WorkflowsControllerDebug.podspec',
:configurations => ['Debug']

pod 'WorkflowControllerRelease',
:podspec => 'https://storage.googleapis.com/workflows-mobile-sdk-artifacts/WorkflowiOS/WorkflowController/WorkflowsControllerRelease.podspec',
:configurations => ['Release']
end

Importante: Reemplaza TuApp con el nombre del target de tu aplicación.

danger

Si no tienes acceso al repositorio del SDK, por favor contacta a tu administrador de cuenta o a soporte, ya que necesitarás acceso al repositorio privado del SDK.

Agrega el siguiente bloque post_install al final de tu Podfile, fuera del bloque target de la aplicación.

Asegúrate de reemplazar Pods-TuApp por el nombre del target de tu aplicación, conservando siempre el prefijo Pods-.

Por ejemplo, si tu target se llama WorkflowsiOS, debes utilizar:

'Pods-WorkflowsiOS'

El Podfile debe quedar con una estructura similar a esta:

platform :ios, '13.0'

target 'WorkflowsiOS' do
use_frameworks!

pod 'WorkflowControllerDebug',
:podspec => 'https://storage.googleapis.com/workflows-mobile-sdk-artifacts/WorkflowiOS/WorkflowController/WorkflowsControllerDebug.podspec',
:configurations => ['Debug']

pod 'WorkflowControllerRelease',
:podspec => 'https://storage.googleapis.com/workflows-mobile-sdk-artifacts/WorkflowiOS/WorkflowController/WorkflowsControllerRelease.podspec',
:configurations => ['Release']
end

# Add post_install after the application target
post_install do |installer|
['WorkflowControllerDebug', 'WorkflowControllerRelease'].each do |pod_name|
Dir.glob("Pods/#{pod_name}/MediaPipeTasks*.xcframework").each do |path|
system("mv '#{path}' Pods/")
end
end

installer.pods_project.targets.each do |target|
if target.name == 'Pods-WorkflowsiOS'
['WorkflowControllerDebug', 'WorkflowControllerRelease'].each do |pod_name|
group = target.project.main_group.find_subpath("Pods/#{pod_name}", false)
next unless group

%w(MediaPipeTasksVision.xcframework MediaPipeTasksCommon.xcframework).each do |fw|
ref = group.new_file(fw)
ref.source_tree = 'SOURCE_ROOT'
target.frameworks_build_phase.add_file_reference(ref)
end
end
end

target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'
config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64'
config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO'
end
end

installer.pods_project.save
end

Importante: El bloque post_install no debe colocarse dentro de target 'TuApp' do. Debe agregarse después del end que cierra el target de la aplicación.

Abre una terminal, navega al directorio de tu proyecto y ejecuta el siguiente comando para instalar el SDK:

pod install

Importante: Recomendamos limpiar las dependencias de CocoaPods antes de volver a instalar.

Desde la carpeta ios, ejecuta:

rm -rf Pods
rm Podfile.lock
pod install

Esto asegura que las dependencias nativas se reinstalen correctamente y evita conflictos con versiones previamente instaladas.

Después de ejecutar:

pod install

CocoaPods generará un archivo .xcworkspace.

A partir de este punto, abre el proyecto utilizando el archivo:

WorkflowsiOS.xcworkspace

Importante: Después de instalar las dependencias con CocoaPods, utiliza siempre el archivo .xcworkspace y no el .xcodeproj, ya que el workspace incluye tanto tu aplicación como las dependencias instaladas por CocoaPods.


Configuración opcional de Build Settings​

Si durante la compilación aparece un error relacionado con Sandbox, deny u Operation not permitted, ve a Build Settings > Build Options y configura:

User Script Sandboxing = NO

Esto permite que los scripts de CocoaPods accedan a los archivos necesarios durante la compilación.

Nota: Si tu proyecto compila correctamente, no es necesario cambiar esta opción.

Configuración de Permisos en Info.plist​

Es necesario declarar los permisos que utiliza la aplicación.

Si tu proyecto cuenta con un archivo Info.plist, agrega las siguientes claves:

<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 application requires access to the device location.</string>

En proyectos recientes de Xcode, es posible que el archivo Info.plist no aparezca directamente en el proyecto. En este caso, selecciona:

Signing & Capabilities > + Capability

Agrega las siguientes capabilities:

Camera
Microphone
Location (When In Use)

Configura las descripciones que se mostrarán al usuario cuando la aplicación solicite cada permiso:

Camera:
3D Liveness Detection by FaceTec.

Microphone:
We need access to your microphone to record audio.

Location:
The application requires access to the device location.

#### **Recursos Personalizados**

Para usar recursos personalizados en tu aplicación, 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**

#### **Importación del SDK**

Para usar el SDK en tu código, primero debes importarlo:

```swift
import WorkflowSDK

Verificación de Licencia​

Antes de crear o iniciar WorkflowController, debes configurar primero la licencia del SDK mediante verifyWorkflowsLicense.

La función recibe el certificado y la frase de contraseña como objetos Data.

import WorkflowSDK

WorkflowController.verifyWorkflowsLicense(
cert: certificateData,
passphrase: passphraseData
)

Las credenciales de licencia son proporcionadas de forma privada por REM.

Importante: No almacenes el certificado ni la frase de contraseña directamente en el código fuente, recursos públicos o control de versiones. Para producción, obtén estas credenciales desde un servicio seguro o utiliza un mecanismoismo de almacenamiento seguro como Keychain.

Configuración para pruebas​

Para realizar pruebas locales, puedes convertir temporalmente las credenciales proporcionadas por REM a Base64 y posteriormente obtener los objetos Data requeridos por el SDK.

  • El archivo .pfx debe convertirse completo a Base64.
  • El archivo .pass contiene la frase de contraseña. Su contenido debe convertirse a Base64.
  • Después, ambos valores se decodifican nuevamente a Data antes de llamar a verifyWorkflowsLicense.

Ejemplo:

import Foundation

enum LicenseConfig {

static func getCertBytes() -> Data {
let base64 = "CERTIFICATE_BASE64"

guard let data = Data(base64Encoded: base64) else {
fatalError("Invalid Base64 string for certificate")
}

return data
}

static func getPassphraseBytes() -> Data {
let base64 = "PASSPHRASE_BASE64"

guard let data = Data(base64Encoded: base64) else {
fatalError("Invalid Base64 string for passphrase")
}

return data
}
}

Posteriormente, configura la licencia antes de inicializar WorkflowController:

let cert = LicenseConfig.getCertBytes()
let passphrase = LicenseConfig.getPassphraseBytes()

WorkflowController.verifyWorkflowsLicense(
cert: cert,
passphrase: passphrase
)

Después de verificar la licencia, puedes inicializar el SDK:

let workflowController = WorkflowController(
baseUrl: "https://api.rem.tools",
apiKey: "tu_rem_api_key",
workflowCustomization: workflowCustomization
)

Nota: Esta forma debe utilizarse únicamente para pruebas locales. Base64 no es un mecanismo de seguridad ni cifrado, por lo que estos valores no deben incluirse en el código fuente que se publique, distribuya o almacene en el repositorio.

Configuración de Personalización​

Puedes personalizar la apariencia del SDK mediante WorkflowCustomization:

let workflowCustomization = WorkflowCustomization(
textLocalization: WorkflowCustomization.TextLocalization(
localization: "Localizable.strings"
),
textFont: WorkflowCustomization.TextFont(
headerFont: "Oswald-Bold.ttf",
bodyFont: "Oswald-Regular.ttf",
buttonFont: "Merriweather-Bold.ttf"
),
theme: WorkflowCustomization.Theme(
primaryColor: "#045CC7",
secondaryColor: "#20DAC6",
buttonPrimaryColor: "#045CC7",
buttonSecondaryColor: "#20DAC6",
buttonTextPrimaryColor: "#FFFFFF",
buttonTextSecondaryColor: "#045CC7",
backgroundColor: "#FFFFFF",
facetecOverlayColor: "#FFFFFF",
showHeaderLogo: true
),
images: WorkflowCustomization.Images(
//png or jpg images
logo: "logo.png",
cameraPermissions: "camera.png",
uploadFaceScan: "upload.png",
uploadFaceSuccess: "success.png",
uploadFaceError: "error.png",
photoMathId: "id.png",
photoMathIdBack: "id_back.png",
cameraActiveTorch: "flash_on.png",
cameraInactiveTorch: "flash_off.png",
fingerPrint: "fingerprint.png",
//svg images
successPage: "success.svg",
errorPage: "error.svg",
unavailablePage: "unavailable.svg",
locationPage: "location.svg",
biometricSignPage: "biometric_sign.svg",
enrollFingerprintPage: "enroll_fingerprint.svg",
enrollBasicPage: "enroll_basic.svg",
enrollFullPage: "enroll_full.svg",
authPage: "auth.svg",
livenessPage: "liveness.svg",
faceEnrollment3dPage: "face_enrollment_3d.svg"
)
)

Archivos de localización​

Los textos del SDK pueden ser personalizados utilizando los archivos base de localización.

Descargas oficiales:

Inicialización del WorkflowController​

Inicializa el WorkflowController con la URL base, las API keys y la personalización:

let workflowController = WorkflowController(
baseUrl: "https://api.rem.tools",
apiKey: "tu_rem_api_key",
workflowCustomization: workflowCustomization
)

Iniciar un Workflow​

Para iniciar un workflow, usa el método start con el ID del workflow que deseas iniciar:

workflowController.start(workflowId: "workflow_id") { (success: Bool, error: WorkflowError?) in
if success {
print("Workflow iniciado")
}
else {
print("Error al iniciar el workflow: \(String(describing: error))")
}
}

Manejo de Eventos de Workflow​

Puedes escuchar eventos de workflow mediante closures:

workflowController.onWorkflowEvent() { (workflow: Workflow) in
print("Workflow UUID: \(workflow.uuid)")
}

workflowController.onStepEvent() { (step: Step) in
print("Evento de paso: \(step.status)")
}

Ejemplo Completo​

El siguiente ejemplo muestra cómo verificar la licencia, inicializar el SDK, iniciar un workflow y escuchar sus eventos:

import SwiftUI
import WorkflowSDK

struct ContentView: View {

var body: some View {
VStack {
Button("Iniciar Workflow") {
openWorkflow()
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}

private func openWorkflow() {

// Verify license before initializing the SDK
WorkflowController.verifyWorkflowsLicense(
cert: LicenseConfig.getCertBytes(),
passphrase: LicenseConfig.getPassphraseBytes()
)

// Configure customization
let workflowCustomization = WorkflowCustomization(
// Customization settings
)

// Initialize Workflows SDK
let workflowController = WorkflowController(
baseUrl: "https://api.rem.tools",
apiKey: "tu_rem_api_key",
workflowCustomization: workflowCustomization
)

// Start workflow
workflowController.start(
workflowId: "workflow_id"
) { (success: Bool, error: WorkflowError?) in
print("Workflows START - Workflow started success: \(success)")

if let error = error {
print("Workflows START - Workflow Id Error: \(error.code)")
}
}

// Listen for workflow events
workflowController.onWorkflowEvent() { (workflow: Workflow) in
print("Workflows EVENT - UUID: \(workflow.uuid ?? "nil"), status: \(workflow.status ?? "nil")")

if workflow.status == "success" {
print("Workflows EVENT - Workflow completed successfully")

// Wait briefly before closing the controller to provide visual feedback
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
workflowController.dismiss()
}
}

if let error = workflow.error {
print("Workflows EVENT - Error: \(error)")
}
}

// Listen for step events
workflowController.onStepEvent() { (step: Step) in
print("Workflows STEP - Step: \(step.step ?? "nil"), status: \(step.status ?? "nil")")

if let error = step.error,
step.status == "error" || step.status == "failed" {

print("Workflows STEP - Error, fatal: \(error.fatal), errorId: \(String(describing: error.details?.errorID)), reason: \(String(describing: error.details?.reason)), stack: \(String(describing: error.details?.stack))")

if let extra = error.extra {

// Biometric scan status
if extra.idScanStatus != nil || extra.faceScanStatus != nil {
print("Workflows STEP - Extra, idScanStatus: \(String(describing: extra.idScanStatus)), faceScanStatus: \(String(describing: extra.faceScanStatus))")
}

// Permission error
if let permission = extra.permission {
print("Workflows STEP - Extra, permission denied: \(permission)")

// Dismiss WorkflowController
workflowController.dismiss()
}

// Device security status
if let secure = extra.secure {
print("Workflows STEP - Extra, is device secure: \(secure)")
}

// License status
if let isLicenseValid = extra.isLicenseValid {
print("Workflows STEP - Extra, is license valid: \(isLicenseValid)")
}
}
}
}
}
}

#Preview {
ContentView()
}
tip

Si tienes alguna duda o problema con la integración del SDK, por favor contacta a soporte.


Android​

La integración del SDK de Workflows en tu aplicación Android es un proceso sencillo que consta de los siguientes pasos:

Requisitos​

  • Android API 29 - 36
  • Android Studio Hedgehog+

Configuración​

Configuración de settings.gradle / settings.gradle.kts​

En el archivo settings.gradle o settings.gradle.kts (Project Settings), cambia el modo de repositorios a PREFER_PROJECT:

Groovy (settings.gradle):

dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_PROJECT)
}

Kotlin DSL (settings.gradle.kts):

dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_PROJECT)
}

¿Por qué es necesario?
Esto permite que Gradle use el repositorio privado donde se encuentra el SDK de Workflows. Sin esta configuración, Gradle podría no encontrar o descargar correctamente el SDK.

Configuración de Repositorios​

En el archivo build.gradle o build.gradle.kts de la aplicación, declara de dónde se obtendrán las dependencias:

Groovy (app/build.gradle):

repositories {
google()
mavenCentral()
maven {
url "https://us-central1-maven.pkg.dev/gendra-services/workflows-mobile"
credentials {
username = "_json_key"
password = file("tu_archivo_credenciales.json").text
}
}
}

android {
// Demás configuración existente del proyecto
// ...
}

// Demás contenido del archivo build.gradle
// ...

Kotlin DSL (app/build.gradle.kts):

repositories {
google()
mavenCentral()
maven {
url = uri("https://us-central1-maven.pkg.dev/gendra-services/workflows-mobile")
credentials {
username = "_json_key"
password = file("tu_archivo_credenciales.json").readText()
}
}
}

android {
// Demás configuración existente del proyecto
// ...
}

// Demás contenido del archivo build.gradle.kts
// ...

Importante

El valor de username debe mantenerse como _json_key. No se debe modificar.

En password debes indicar la ruta donde se encuentra el archivo JSON de credenciales.

El archivo de credenciales se utiliza únicamente para que Gradle pueda descargar el SDK.

No agregues el archivo JSON dentro de assets, res o cualquier otra carpeta que pueda incluirlo en el APK/AAB de la aplicación.

También se recomienda agregar este archivo al .gitignore para evitar subir las credenciales al repositorio.

danger

Si no tienes acceso al repositorio o al archivo de credenciales, por favor contacta a tu administrador de cuenta o a soporte, ya que necesitarás acceso al repositorio privado del SDK.

Implementación de Dependencias​

Agrega las dependencias de Workflows en el archivo build.gradle o build.gradle.kts de la aplicación:

Groovy (app/build.gradle):

dependencies {
// Workflows SDK
implementation ('tools.rem.workflows:sdk:1.22.6@aar') { transitive = true }

// Core dependencies
implementation 'androidx.appcompat:appcompat:1.7.1'
implementation 'com.google.android.material:material:1.13.0'
implementation 'com.getkeepsafe.relinker:relinker:1.4.5'
implementation "androidx.window:window:1.0.0"
implementation 'com.google.android.gms:play-services-location:21.3.0'
implementation 'com.squareup.okhttp3:okhttp:5.1.0'
implementation 'com.squareup.okio:okio:3.16.0'
implementation 'com.google.code.gson:gson:2.13.1'
implementation "com.bugsnag:bugsnag-android:6.17.0"
implementation 'com.google.mediapipe:tasks-vision:0.10.32'

// Media dependencies
implementation "androidx.media3:media3-exoplayer:1.4.1"
implementation "androidx.media3:media3-exoplayer-hls:1.4.1"
implementation "androidx.media3:media3-exoplayer-dash:1.4.1"
implementation "androidx.media3:media3-exoplayer-rtsp:1.4.1"
implementation "androidx.media3:media3-exoplayer-smoothstreaming:1.4.1"

// Camera dependencies
implementation "com.otaliastudios:cameraview:2.7.2"
implementation "androidx.camera:camera-core:1.5.3"
implementation "androidx.camera:camera-camera2:1.5.3"
implementation "androidx.camera:camera-lifecycle:1.5.3"
implementation "androidx.camera:camera-video:1.5.3"
implementation "androidx.camera:camera-view:1.5.3"
implementation "androidx.camera:camera-extensions:1.5.3"

// Utilities dependencies
implementation 'com.google.guava:guava:27.1-android'
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2'
}

Kotlin DSL (app/build.gradle.kts):

dependencies {
// Workflows SDK
implementation("tools.rem.workflows:sdk:1.22.6@aar") {
isTransitive = true
}

// Core dependencies
implementation("androidx.appcompat:appcompat:1.7.1")
implementation("com.google.android.material:material:1.13.0")
implementation("com.getkeepsafe.relinker:relinker:1.4.5")
implementation("androidx.window:window:1.0.0")
implementation("com.google.android.gms:play-services-location:21.3.0")
implementation("com.squareup.okhttp3:okhttp:5.1.0")
implementation("com.squareup.okio:okio:3.16.0")
implementation("com.google.code.gson:gson:2.13.1")
implementation("com.bugsnag:bugsnag-android:6.17.0")
implementation("com.google.mediapipe:tasks-vision:0.10.32")

// Media dependencies
implementation("androidx.media3:media3-exoplayer:1.4.1")
implementation("androidx.media3:media3-exoplayer-hls:1.4.1")
implementation("androidx.media3:media3-exoplayer-dash:1.4.1")
implementation("androidx.media3:media3-exoplayer-rtsp:1.4.1")
implementation("androidx.media3:media3-exoplayer-smoothstreaming:1.4.1")

// Camera dependencies
implementation("com.otaliastudios:cameraview:2.7.2")
implementation("androidx.camera:camera-core:1.5.3")
implementation("androidx.camera:camera-camera2:1.5.3")
implementation("androidx.camera:camera-lifecycle:1.5.3")
implementation("androidx.camera:camera-video:1.5.3")
implementation("androidx.camera:camera-view:1.5.3")
implementation("androidx.camera:camera-extensions:1.5.3")

// Utilities dependencies
implementation("com.google.guava:guava:27.1-android")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2")
}

Compatibilidad con el tema de la aplicación​

Para evitar conflictos entre el tema de la aplicación y el tema incluido por el SDK, agrega el atributo tools:replace dentro de la etiqueta <application>:

tools:replace="android:theme"

También es necesario declarar el namespace tools en la etiqueta <manifest>.

Ejemplo completo:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<application
android:theme="@style/Theme.YourApplication"
tools:replace="android:theme">

<!-- Demás configuración de la aplicación -->

</application>

</manifest>

Configuración de Release Build​

Para asegurar el correcto funcionamiento del SDK en compilaciones release, agrega la siguiente configuración dentro del bloque android del archivo build.gradle o build.gradle.kts de la aplicación.

Groovy (app/build.gradle):

android {
// Demás configuración...

buildTypes {
release {
shrinkResources false

// Demás configuración de release...
}
}

aaptOptions {
noCompress "tflite", "task"
}

sourceSets {
main {
assets.srcDirs += 'src/main/assets'
}
}
}

Kotlin DSL (app/build.gradle.kts):

android {
// Demás configuración...

buildTypes {
release {
isShrinkResources = false

// Demás configuración de release...
}
}

androidResources {
noCompress += listOf("tflite", "task")
}

sourceSets {
getByName("main") {
assets.srcDir("src/main/assets")
}
}
}

Esta configuración evita que algunos recursos utilizados por el SDK sean eliminados o comprimidos durante la generación del APK o AAB.

danger

No excluyas librerías .so requeridas por el SDK usando packagingOptions o packaging.jniLibs.

Por ejemplo, NO debes usar:

Groovy (app/build.gradle):

packagingOptions {
exclude 'lib/arm64-v8a/libPhoenixAndroid.so'
}

Kotlin DSL (app/build.gradle.kts):

packaging {
jniLibs {
excludes += "lib/arm64-v8a/libPhoenixAndroid.so"
}
}

El SDK depende de librerías nativas como:

lib/arm64-v8a/libPhoenixAndroid.so

Si se excluyen, la aplicación puede compilar pero fallará en ejecución, especialmente en la inicialización de biometría.

Configuración de R8 Keep Rules / ProGuard​

Agrega las siguientes reglas en el archivo de configuración correspondiente a tu proyecto.

En proyectos recientes de Android:

app/src/main/keepRules/rules.keep

En proyectos con la configuración tradicional:

app/proguard-rules.pro

Agrega las siguientes reglas:

# 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 tools.rem.workflows_flutter_plugin.Models.Step.Step { *; }
-keep class tools.rem.workflows_flutter_plugin.Models.Workflow.Workflow { *; }
-keep class tools.rem.workflows_flutter_plugin.Models.WorkflowError { *; }
-keep class tools.rem.workflows_flutter_plugin.WorkflowsFlutterPlugin { *; }
-keepclassmembers class tools.rem.workflows_flutter_plugin.Models.Workflow.Workflow {
public <init>(...);
}
-keep class tools.rem.workflows_flutter_plugin.Models.ConfigFacetec { *; }
-keepclassmembers class tools.rem.workflows_flutter_plugin.Models.ConfigFacetec {
public <init>(...);
}

#Keep the mediapipe classes
-keep public class com.google.mediapipe.framework.Graph.** { *; }
-keep interface com.google.common.** { *; }
-keep class * extends com.google.common.flogger.** {*;}
-keep class com.google.common.** { *; }
-keep class com.google.common.flogger.** { *; }
-keep class com.google.mediapipe.tasks.** { *; }
-keep class com.google.mediapipe.tasks.vision.** { *; }
-keep class com.google.mediapipe.tasks.core.** { *; }
-keep class com.google.mediapipe.framework.ProtoUtil$* { *; }
-keep class com.google.mediapipe.framework.** { *; }
-keep class com.google.mediapipe.framework.image.** { *; }
-keep class com.google.mediapipe.** { *; }
-keep class com.google.mediapipe.proto.** { *; }
-keep class javax.lang.model.** { *; }
-keep class com.google.mediapipe.solutioncore.** { *; }
-keep class com.google.protobuf.** { *; }
-keep class org.tensorflow.lite.** { *; }
-dontwarn com.google.mediapipe.proto.CalculatorProfileProto$CalculatorProfile
-dontwarn com.google.mediapipe.proto.GraphTemplateProto$CalculatorGraphTemplate
-dontwarn org.tensorflow.lite.**
-dontwarn javax.annotation.**
-dontwarn javax.lang.model.**
-keepclassmembers class * extends com.google.protobuf.GeneratedMessageLite.** { *; }
-keepclassmembers class com.google.mediapipe.**$$ExternalSyntheticLambda* { *; }
-keepattributes InnerClasses,EnclosingMethod,Signature,*Annotation*

#Keep the okhttp3 classes
-keep class okhttp3.** { *; }
-keep class okio.** { *; }

#Keep the gson classes
-keep class com.google.gson.** { *; }

#Keep the facetec classes
-dontwarn javax.annotation.Nullable
-dontwarn com.facetec.sdk.**
-keep,includecode,includedescriptorclasses class com.facetec.sdk.** { *; }

#Keep the cameraview classes
-keep class com.otaliastudios.cameraview.** { *; }
-dontwarn com.otaliastudios.cameraview.**

Recursos Personalizados​

Para usar recursos personalizados, agrega los assets en la carpeta res según su tipo:

CarpetaTipo de RecursoFormatos
drawableImágenes.png, .jpeg
fontTipografías.ttf, .otf
rawImágenes vectoriales.svg
xmlTextos localizados.xml

Uso​

Importación del SDK​

Para usar el SDK en tu código Kotlin, primero debes importarlo:

import tools.rem.workflows.WorkflowsActivity
import tools.rem.workflows.model.WorkflowCustomization
import tools.rem.workflows_flutter_plugin.Models.Step.Step
import tools.rem.workflows_flutter_plugin.Models.Workflow.Workflow
import tools.rem.workflows_flutter_plugin.Models.WorkflowError

Verificación de Licencia​

Antes de crear o iniciar WorkflowsActivity, debes configurar primero la licencia del SDK mediante verifyWorkflowsLicense.

La función recibe el certificado y la frase de contraseña como arreglos de bytes (ByteArray en Kotlin / byte[] en Java).

import tools.rem.workflows.WorkflowsActivity

WorkflowsActivity.verifyWorkflowsLicense(
certificateBytes,
passphraseBytes
)

Las credenciales de licencia son proporcionadas de forma privada por REM.

Importante: No almacenes el certificado ni la frase de contraseña directamente en el código fuente, recursos públicos o control de versiones. Para producción, obtén estas credenciales desde un servicio seguro o utiliza un mecanismo de almacenamiento seguro respaldado por Android Keystore.

Configuración para pruebas​

Para realizar pruebas locales, puedes convertir temporalmente las credenciales proporcionadas por REM a Base64 y posteriormente obtener los ByteArray requeridos por el SDK.

  • El archivo .pfx debe convertirse completo a Base64.
  • El archivo .pass contiene la frase de contraseña. Su contenido debe convertirse a Base64.
  • Después, ambos valores se decodifican nuevamente a ByteArray antes de llamar a verifyWorkflowsLicense.

Ejemplo:

import android.util.Base64

object LicenseConfig {

fun getCertBytes(): ByteArray {
val base64 = "CERTIFICATE_BASE64"
return Base64.decode(base64, Base64.DEFAULT)
}

fun getPassphraseBytes(): ByteArray {
val base64 = "PASSPHRASE_BASE64"
return Base64.decode(base64, Base64.DEFAULT)
}
}

Posteriormente, configura la licencia:

WorkflowsActivity.verifyWorkflowsLicense(
LicenseConfig.getCertBytes(),
LicenseConfig.getPassphraseBytes()
)

Nota: Esta forma debe utilizarse únicamente para pruebas locales. Base64 no es un mecanismo de seguridad ni cifrado, por lo que estos valores no deben incluirse en el código fuente que se publique, distribuya o almacene en el repositorio.

Configuración de Personalización​

Puedes personalizar la apariencia del SDK mediante WorkflowCustomization:

val workflowCustomization = WorkflowCustomization(
textLocalization = WorkflowCustomization.TextLocalization(
localization = R.xml.localizable
),
textFont = WorkflowCustomization.TextFont(
headerFont = R.font.oswald_bold,
bodyFont = R.font.oswald_regular,
buttonFont = R.font.merriweather_bold
),
theme = WorkflowCustomization.Theme(
primaryColor = "#045CC7",
secondaryColor = "#20DAC6",
buttonPrimaryColor = "#045CC7",
buttonSecondaryColor = "#20DAC6",
buttonTextPrimaryColor = "#FFFFFF",
buttonTextSecondaryColor = "#045CC7",
backgroundColor = "#FFFFFF",
facetecOverlayColor = "#FFFFFF",
showHeaderLogo = true
),
images = WorkflowCustomization.Images(
//png or jpg images
logo = R.drawable.logo,
cameraPermissions = R.drawable.camera,
uploadFaceScan = R.drawable.upload,
uploadFaceSuccess = R.drawable.success,
uploadFaceError = R.drawable.error,
photoMathId = R.drawable.photo_match_id_image,
photoMathIdBack = R.drawable.photo_match_id_back_image,
cameraActiveTorch = R.drawable.flash_active,
cameraInactiveTorch = R.drawable.flash_inactive,
fingerPrint = R.drawable.flash_inactive,
//svg images
successPage = R.raw.success_image,
errorPage = R.raw.error_image,
unavailablePage = R.raw.unavailable_image,
locationPage = R.raw.geolocation_image,
biometricSignPage = R.raw.biometric_sign_image,
enrollFingerprintPage = R.raw.biometric_sign_image,
enrollBasicPage = R.raw.id_scan_image,
enrollFullPage = R.raw.id_scan_image,
authPage = R.raw.biometric_sign_image,
livenessPage = R.raw.biometric_sign_image,
faceEnrollment3dPage = R.raw.biometric_sign_image
)
)

Archivos de localización​

Los textos del SDK pueden ser personalizados utilizando los archivos base de localización.

Descarga el archivo localizable.xml ejecutando:

curl -L "https://storage.googleapis.com/workflows-mobile-sdk-artifacts/workflows/localization/localizable.xml" -o localizable.xml

Inicialización del WorkflowController​

Inicializa el WorkflowsActivity con la URL base, las API keys y la personalización:

val workflowController = WorkflowsActivity(
"https://api.rem.tools", // Base URL
"tu_rem_api_key", // REM API Key
workflowCustomization // Personalización
)

Iniciar un Workflow​

Para iniciar un workflow, usa el método start con el ID del workflow y el contexto de la actividad:

workflowController.start(
"workflow_id",
this
) { success: Boolean, error: WorkflowError? ->
if (success) {
Log.d("Workflow", "Workflow iniciado exitosamente")
}
else {
Log.e("Workflow", "Error al iniciar el workflow: $error")
}
}

Manejo de Eventos de Workflow​

Puedes escuchar eventos de workflow mediante callbacks:

workflowController.onStepEvent { step: Step ->
Log.d("Workflow", "Evento de paso: ${step}")
}

workflowController.onWorkflowEvent { workflow: Workflow ->
Log.d("Workflow", "Evento de workflow: ${workflow}")
}

Ejemplo Completo​

El siguiente ejemplo muestra cómo verificar la licencia, inicializar el SDK, iniciar un workflow y escuchar sus eventos:

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

// Verify license before initializing the SDK
WorkflowsActivity.verifyWorkflowsLicense(LicenseConfig.getCertBytes(), LicenseConfig.getPassphraseBytes())

// Configure customization
val workflowCustomization = WorkflowCustomization(
// Customization settings
)

// Initialize Workflows SDK
val workflowController = WorkflowsActivity(
"https://api.rem.tools",
"tu_rem_api_key",
workflowCustomization
)

// Start workflow
workflowController.start(
"workflow_id",
this
) { success: Boolean, error: WorkflowError? ->
Log.d("Workflows START", "Workflow started success: $success")

if (error != null) {
Log.d("Workflows START", "Workflow Id Error: ${error.ordinal}")
}
}

// Listen for workflow events
workflowController.onWorkflowEvent { workflow: Workflow ->
Log.d("Workflows EVENT", "UUID: ${workflow.uuid}, status: ${workflow.status}")

if (workflow.status == "success") {
Log.d("Workflows EVENT", "Workflow completed successfully")
lifecycleScope.launch {
// It is recommended to wait briefly before closing the activity to provide visual feedback to the user
delay(2000.milliseconds)
// Dismiss WorkflowsActivity
WorkflowsActivity.dismiss()
}
}
}

// Listen for step events
workflowController.onStepEvent { step: Step ->
Log.d("Workflows STEP", "Step: ${step.step}, status: ${step.status}")
val error = step.error
if (error != null && (step.status == "error" || step.status == "failed")) {
Log.d("Workflows STEP", "Error, fatal: ${error.fatal}, errorId: ${error.details?.errorId}, reason: ${error.details?.reason}, stack: ${error.details?.stack}")

error.extra?.let { extra ->
// Biometric scan status
if (extra.idScanStatus != null || extra.faceScanStatus != null) {
Log.d("Workflows STEP", "Extra, idScanStatus: ${extra.idScanStatus}, faceScanStatus: ${extra.faceScanStatus}")
}

// Permission error
if (extra.permission != null) {
Log.d("Workflows STEP", "Extra, permission denied: ${extra.permission}")

// Dismiss WorkflowsActivity
WorkflowsActivity.dismiss()
}

// Device security status
if (extra.secure != null) {
Log.d("Workflows STEP", "Extra, is device secure: ${extra.secure}")
}

// License status
if (extra.isLicenseValid != null) {
Log.d("Workflows STEP", "Extra, is license valid: ${extra.isLicenseValid}")
}
}
}
}
}
}
tip

Si tienes alguna duda o problema con la integración del SDK, por favor contacta a soporte.

rem

JOIN US ON

facebooktwitterLinkedIn
CONTACThello@rem.tools55-91-03-80-54
© Copyright rem 2022 All rights reserved.