React Native

O React Native usa um sistema flexível que permite chamar APIs específicas da plataforma, estejam elas disponíveis em código Kotlin ou Java no Android ou em código Swift ou Objective-C no iOS.

Este guia ensina como integrar o AllowMeSDK ao seu aplicativo React Native utilizando o mecanismo de NativeModule.

📘

Importante!

O tutorial abaixo possui simplificações por se tratar do código de um app de exemplo. Os resultados das chamadas são mostrados na tela para facilitar o entendimento. Você deverá fazer as modificações necessárias para a sua implementação real. Além disso, o código completo de exemplo está disponível. Entre em contato com [email protected] para ter acesso.

1. Implementação Nativa Android

Antes de prosseguir nessa seção é recomendado a leitura da integração nativa para Android.

Antes de tudo, abra o projeto Android do seu aplicativo React Native no Android Studio:

1.1 Adicionando o AllowMeSDK como dependência

Essa seção se refere à implementação em React Native do que é mostrado com mais detalhes em Primeiros Passos - Adicionando AllowMeSDK como dependência da documentação nativa.Com o projeto aberto, você deve adicionar o SDK e suas dependências nos arquivos build.gradle.

O AllowMeSDK está hospedado em um repositório privado Maven, acessível via Gradle. Por isso, você deve adicionar ao build.gradle do seu projeto as seguintes linhas, contendo o username, password e URL:

allprojects {
  repositories {
    mavenLocal()
    google()
    maven { url 'https://jitpack.io' }
    mavenCentral()

    maven {
      authentication {
        basic(BasicAuthentication)
      }
      credentials {
        username 'SEU_USERNAME_AQUI'
        password 'SEU_PASSWORD_AQUI'
      }
      url "https://android.allowmecloud.com/sdk"
    }
  }
}

Em seguida, você deve adicionar essas dependências ao build.gradle do seu módulo:

📘

Build Types

Se você estiver usando builds com nomes diferentes de stage e/ou release, acrescente o campo a seguir:

buildTypes {    
  buildParaHomolog {
    matchingFallbacks = ['stage']
  }

  buildParaRelease {
    matchingFallbacks = ['release']
  }
}

1.2 Criando uma Bridge

Essa seção se refere à implementação em React Native do que é mostrado com mais detalhes em Primeiros Passos - Inicializar o SDK da documentação nativa.O primeiro passo é criar o arquivo do módulo nativo, que neste exemplo chamaremos de AllowMeBridge. Nesse módulo, faremos também a implementação básica do AllowMe com o método setup, responsável pela troca de chaves da criptografia.

import {...};

class AllowMeBridge(context: ReactApplicationContext) : ReactContextBaseJavaModule(context) {
  private val mReactContext: ReactApplicationContext = context
  private var mAllowme: AllowMe? = null

  {...}

  override fun getName() = "AllowMeBridge"
  
  @ReactMethod
  fun initSetup(promise: Promise) {
    val currentActivity: Activity? = currentActivity
    if (currentActivity != null) {
      ActivityCompat.requestPermissions(
        currentActivity,
        arrayOf(Manifest.permission.CAMERA),
        1
      )
    }

    runAllowMeCommand(object : AllowMeCommand {
      override fun exec() {
        mAllowme?.setup({
          promise.resolve("setup success!")
        }) {
          promise.reject(it)
        }
      }
    })
  }

  private fun lazyAllowMeInit() {
    if (mAllowme == null) {
      mAllowme = AllowMe.getInstance(mReactContext, BuildConfig.API_KEY)
    }
  }

  private fun runAllowMeCommand(command: AllowMeCommand) {
    mReactContext.runOnUiQueueThread {
      lazyAllowMeInit()
      command.exec()
    }
  }

  private interface AllowMeCommand {
    fun exec()
  }
}

Como você pode ver, a classe AllowMeBridge herda da classe ReactContextBaseJavaModule e implementa as funcionalidades exigidas pelo React Native. Todos os métodos de módulo nativo destinados a serem chamados no TypeScript devem ser anotados com @ReactMethod.

Além disso, a função lazyAllowMeInit inicializa a variável mAllowme sempre que for necessário.

Também é necessário implementar o método getName(), que retorna uma String com o nome do módulo nativo, podendo assim ser acessado em seu TypeScript desse modo:

import { NativeModules } from 'react-native';

const {AllowMeBridge} = NativeModules;

1.3. Implementando o Device Intelligence (Contextual)

Essa seção se refere à implementação em React Native do que é mostrado com mais detalhes em Device Intelligence (Contextual) da documentação nativa.Com base no exemplo mostrado na seção anterior, podemos adicionar o código de exemplo abaixo para adicionar o produto Device Intelligence (Contextual) do AllowMeSDK com uma chamada ao método collect.

{...}

@ReactMethod
fun collect(promise: Promise) {
  runAllowMeCommand(object : AllowMeCommand {
    override fun exec() {
      mAllowme?.collect({
        promise.resolve(it)
      }) {
        promise.reject(it)
      }
    }
  })
}

{...}

1.4. Implementando Validação de Endereço

Essa seção se refere à implementação em React Native do que é mostrado com mais detalhes em Validação de Endereço da documentação nativa.Com base no exemplo mostrado na seção anterior, podemos adicionar o código de exemplo abaixo para adicionar a função Validação de Endereço do AllowMeSDK. Observe que o método start tem como objetivo realizar o agendamento das coletas periódicas enquando o addPerson faz o envio das informações do usuário para o backend AllowMe para realização da validação de endereço.

{...}

@ReactMethod
fun start(promise: Promise) {
  runAllowMeCommand(object : AllowMeCommand {
    override fun exec() {
      mAllowme?.start({
        promise.resolve("start onboarding finished!")
      }) {
        promise.reject(it)
      }
    }
  })
} 

@ReactMethod
fun addPerson(promise: Promise) {
  runAllowMeCommand(object : AllowMeCommand {
    val address: Address = Address(
      "Nome da Rua",
      "Cidade",
      "XX",
      0,
      "Bairro",
      "0000-000",
      "",
      "",
      null
    )
    val person: Person = Person(
      "Nome da pessoa",
      "00000000000",
      address,
      "[email protected]",
      "000000000"
    )

    override fun exec() {
      mAllowme?.addPerson(person, {
        promise.resolve("addPerson onboarding finished!")
      }) {
        promise.reject(it)
      }
    }
  })
}

{...}

Neste exemplo, o método addPerson que criamos instancia as classes Person e Address com valores pré-definidos. Esses valores, porém, devem ser fornecidos pela sua aplicação React Native. Mais informações sobre os parâmetros aceitos pelo método podem ser encontradas em Cadastrando uma pessoa.

1.5. Implementando a Biometria Facial

Essa seção se refere à implementação em React Native do que é mostrado com mais detalhes em Biometria Facial da documentação nativa.Antes de tudo, é necessário adicionar a permissão da câmera como dito na documentação nativa do Android. Na nossa implementação tomamos a decisão de adicioná-la junto ao método setup, mas fique livre para adequar ao seu aplicativo.

val currentActivity: Activity? = currentActivity

if (currentActivity != null) {
  ActivityCompat.requestPermissions(
    currentActivity,
    arrayOf(Manifest.permission.CAMERA),
    1
  )
}

O próximo passo será chamar a Activity responsável pela captura das fotos como mostra o código abaixo:

private var mPromise: Promise? = null
{...}

@ReactMethod
fun showBiometrics(promise: Promise) {
  runAllowMeCommand(object : AllowMeCommand {
    override fun exec() {
      val currentActivity = currentActivity as? AppCompatActivity
      mPromise = promise
     
      if (currentActivity != null) {
        mAllowme?.startBiometrics(currentActivity) { biometricsResult ->
          if (biometricsResult.errorType == null) {
            //REQUEST TO SERVER
            var result = ("payload: " + biometricsResult.payload) + " | "
            for (path in biometricsResult.paths) {
                result += "$path & "
            }

            mPromise?.resolve(result)
          } else {
            // biometricsResult.errorType
            mPromise?.reject("Biometrics Error", biometricsResult.errorType?.message)
          } 
      }
    }
  })
}

{...}

Observe que criamos uma variável chamada mPromise na classe para receber o resultado da biometria.

O resultado acima é transformado em String para ser mostrado na tela a partir do código em dart. Faça as modificações necessárias para a sua implementação!

📘

TIP

Para informações de como implementar a Biometria Facial com provider Facetec, veja as seções 2.1. Iniciando a Biometria Facial da Facetec, 3.1 Tratando erros gerados pelo provedor (Facetec) e 5. Customização de Layout - Facetec.

1.6. Registrando a Bridge e o Package

Depois da criação da bridge, adicione-a a um ReactPackage criando uma nova classe Java chamada AllowMeReactPackage.kt que implementa ReactPackage.

import {...}

class AllowMeReactPackage : ReactPackage {
  override fun createViewManagers(
    reactContext: ReactApplicationContext
  ): MutableList<ViewManager<View, ReactShadowNode<*>>> = mutableListOf()

  override fun createNativeModules(
    reactContext: ReactApplicationContext
  ): MutableList<NativeModule> = listOf(AllowMeBridge(reactContext)).toMutableList()
}

Após a implementação do AllowMeReactPackage, adicione-o à lista de pacotes retornados no método getPackages() de ReactNativeHost. Esse arquivo se encontra na classe MainApplication.kt

import {...};

class MainApplication : Application(), ReactApplication {

  override val reactNativeHost: ReactNativeHost =
    object : DefaultReactNativeHost(this) {
      override fun getPackages(): List<ReactPackage> =
        PackageList(this).packages.apply {
          add(AllowMeReactPackage())
        }

      {...}
    }

  {...}
}

2. Implementação Nativa iOS

Antes de prosseguir nessa seção é recomendado a leitura da integração nativa para iOS v2.

Para começar, abra o projeto iOS em seu aplicativo React Native no Xcode:

2.1. Adicionando o AllowMeSDK como dependência

Essa seção se refere à implementação em React Native do que é mostrado com mais detalhes em Primeiros Passos - Adicionando AllowMeSDK como dependência da documentação nativa.Antes de começar, certifique-se de que esteja de posse das credenciais de acesso aos nossos repositórios!

❗️

WARNING

Caso não faça ideia de onde estejam essas credenciais, entre em contato com o nosso suporte em [email protected].

Você terá duas opções de SDK: o AllowMeSDK de homologação e o de produção. A versão de homologação deve ser usada para testes e debug, enquanto a de produção deve ser enviada para a AppStore junto ao seu aplicativo. Você deve adicionar o AllowMeSDKHomolog ao seu target de Debug!

O nosso SDK tem suporte para Swift Package Manager e Cocoapods. Escolha o que for melhor para o seu projeto, acesse o arquivo xcworkspace no Xcode os passos descritos na documentação nativa:

Swift Package ManagerCocoapods

2.2. Criando uma Bridge

Essa seção se refere à implementação em React Native do que é mostrado com mais detalhes em Primeiros Passos - Importar e Inicializar o SDK da documentação nativa.Antes de tudo, importe o AllowMe de acordo com a configuração usada. Lembre-se que você deve adicionar o AllowMeSDKHomolog ao seu target de debug/homologação e o AllowMeSDK ao target de release/produção.

#if DEBUG
import AllowMeSDKHomolog
let ALLOWE_API_KEY = "your-homolog-api-key-here"
#else
import AllowMeSDK
let ALLOWE_API_KEY = "your-prod-api-key-here"
#endif

Após isso, o primeiro passo é criar o arquivo do módulo nativo, que neste exemplo chamaremos de AllowMeBridge. Faremos a implementação em Objective-C, o que significa que criaremos inicialmente o AllowMeBridge.h como mostrado abaixo:

#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>

#if DEBUG
import AllowMeSDKHomolog
let ALLOWE_API_KEY = "your-homolog-api-key-here"
#else
import AllowMeSDK
let ALLOWE_API_KEY = "your-prod-api-key-here"
#endif

@interface AllowMeBridge : NSObject <RCTBridgeModule>

@property (nonatomic, weak) AllowMe *allowMe;

@end

E, em seguida, criaremos o arquivo de implementação correspondente AllowMeBridge.m na mesma pasta. Por enquanto, o nosso módulo nativo AllowMeBridge.m inclui apenas uma macro RCT_EXPORT_MODULE, que exporta e registra a classe do módulo nativo para o React Native. A macro RCT_EXPORT_MODULE também aceita um argumento opcional que especifica o nome pelo qual o módulo estará acessível em seu código TypeScript. Para esse exemplo vamos manter com o valor default.

Vale ressaltar que o React Native não exportará nenhum método em um módulo nativo para o TypeScript a menos que seja explicitamente informado com a utilização da macro RCT_EXPORT_METHOD.

#import "AllowMeBridge.h"

@implementation AllowMeBridge

// Exporta um módulo chamado AllowMeBridge
RCT_EXPORT_MODULE();

@end

Nesse arquivo implementaremos a primeira função do AllowMe: o setup, que é usado para realizar a troca de chaves que garante a segurança de todos os nossos produtos. Marcaremos ele como RCT_EXPORT_METHOD.

@implementation AllowMeBridge
AllowMe* mAllowMe;
RCTPromiseRejectBlock mReject;

RCT_EXPORT_MODULE();

RCT_EXPORT_METHOD(initSetup:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject) {
  mReject = reject;
  [self getAllowMeInstance];
  
  [mAllowMe setupWithCompletion:^(NSError * setupError) {
    if(setupError == nil) {
      resolve(@"setup success");
    } else {
      reject(@"Setup Error",
             [setupError localizedDescription],
             setupError);
    }
  }];
};

- (void) getAllowMeInstance {
  if (mAllowMe == nil) {
    NSError* error = nil;
    mAllowMe = [AllowMe getInstanceWithApiKey:ALLOWME_API_KEY error: &error];
    
    if(error != nil) {
      mReject(@"AllowMe Instance Error",
              [error localizedDescription],
              error);
    }
  }
  
}

Note que implementamos tmabém a função getAllowMeInstance, que inicializa a variável mAllowme sempre que for necessário. Além disso,a fim de retornar possíveis erros na inicialização para o app React Native, escolhemos criar uma nova variável para armazenar o reject.

Agora, verifique os produtos contratados para seguir na implementação mostrada nas próximas seções!

2.3. Implementando o Device Intelligence (Contextual)

Essa seção se refere à implementação em React Native do que é mostrado com mais detalhes em Device Intelligence (Contextual) da documentação nativa.Tomando o código anterior como base, vamos adicionar um novo método para realizar a chamada ao método collect que retorna uma string criptografada contendo o fingerprint gerado pelo AllowMe. Observe o exemplo com o código abaixo:

RCT_REMAP_METHOD(collect, withResolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
  
  mReject = reject;
  [self getAllowMeInstance];
  
  [mAllowMe collectOnSuccess:^(NSString * collect) {
    resolve(collect);
  } onError:^(NSError * collectError) {
    reject(@"Collect Error", [collectError localizedDescription], collectError);
  }];
};

2.4. Implementando Validação de Endereço

Essa seção se refere à implementação em React Native do que é mostrado com mais detalhes em Validação de Endereço da documentação nativa.Ao contrário dos métodos mencionados anteriormente, o método start de agendamento de coletas para a função de Validação de Endereço deve apenas ser chamado durante a inicialização do app iOS no arquivo AppDelegate.mm

{...}

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  {...}

  NSError* error = nil;
  AllowMe* allowMe = [AllowMe getInstanceWithApiKey:ALLOWME_API_KEY error: &error];
  
  if(error == nil) {
    NSError* startError = [allowMe start];
    if(startError != nil) {
      NSLog(@"Start Error: %@", [startError localizedDescription]);
    }
  } else {
    NSLog(@"AllowMe Instance Error: %@",[error localizedDescription]);
  }

  {...}
}

❗️

IMPORTANTE!!!

Verifique a documentação nativa para entender porque o start deve ser chamado sempre durante a inicialização e realizar a configuração necessária de Background Modes. Observe também na documentação nativa a necessidade de chamar, ou não, o método abaixo conforme fizemos com o método application

- (void)application:(UIApplication *)application performFetchWithCompletionHandler:
(void (^)(UIBackgroundFetchResult))completionHandler {
  NSError* error = nil;
  AllowMe* allowMe = [AllowMe getInstanceWithApiKey:ALLOWME_API_KEY error: &error];

  if(error == nil) {
    [allowMe application:application performFetchWithCompletionHandler:completionHandler];
  }
}

Após isso, de volta ao AllowMeBridge.m implementaremos a função addPerson:

RCT_EXPORT_METHOD(addPerson:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject) {
  PersonModel* person = [self getPerson];
  
  mReject = reject;
  [self getAllowMeInstance];
  
  [mAllowMe addPersonWithPerson:person completion:^(NSError * error) {
    if(error == nil) {
      resolve(@"Pessoa adicionada com sucesso");
    } else {
      reject(@"Onboarding Error", [error localizedDescription], error);
    }
  }];
};

- (PersonModel*)getPerson {
  AddressModel *address = [
    [AddressModel alloc] initWithStreet:@"Rua"
    number:@"000"
    neighbourhood:@"Bairro"
    city: @"Cidade"
    state: @"AA"
    zipCode: @"00000000"
  ];
  
  PersonModel *person = [
    [PersonModel alloc] initWithName:@"Nome"
    nationalId:@"00000000000"
    address:address
  ];
  
  return person;
};

Veja a documentação nativa da Validação de Endereço para ver todos os dados que podem ser passados para o método addPerson a partir de sua aplicação React Native.

2.5. Implementando a Biometria Facial

Essa seção se refere à implementação em React Native do que é mostrado com mais detalhes em Biometria Facial da documentação nativa.Do mesmo modo, vamos criar uma função para inicializar a tela da Biometria Facial a partir da nossa implementação nativa

@implementation AllowMeBridge
AllowMe* mAllowMe;
RCTPromiseRejectBlock mReject;
RCTPromiseResolveBlock mPromise;

RCT_EXPORT_MODULE();

RCT_EXPORT_METHOD(showBiometrics:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject) {
  dispatch_async(dispatch_get_main_queue(), ^{
    mResolve = resolve;
    mReject = reject;
    
    [self getAllowMeInstance];
    
    AllowMeBiometricsConfig* config = [[AllowMeBiometricsConfig alloc] init];
    
    UIWindow *window = (UIWindow*)[[UIApplication sharedApplication] windows][0];
    UIViewController* vc = window.rootViewController;
    [mAllowMe startBiometricsWithViewController:vc delegate:self config:config];
  });
}

📘

TIP

Observe a documentação nativa da Biometria Facial para verificar todas as possibilidades de customização disponíveis com a AllowMeBiometricsConfig.


Observe que o código da chamada da Biometria Facial é feito dentro de um dispatch_async para garantir que ele será feito na Main Thread. Por se tratar de uma interação visual, o iOS nos obriga a realizar esse tipo de operação na thread Main ou o app pode crashar.

Para receber o resultado das capturas, precisamos implementar o delegate que foi passado para o startBiometricsWithViewController. Você pode passar qualquer classe que implemente o AllowMeBiometricsDelegate. No nosso caso, passamos a própria AllowMeBridge e atualizamos o protocolo em AllowMeBridge.h

@interface AllowMeBridge : NSObject <RCTBridgeModule, AllowMeBiometricsDelegate>
  {...}
@end

De volta ao AllowMeBridge.m devemos implementar a função do protocolo:

- (void) biometricsDidFinishWithBiometricsObject:(AllowMeBiometricsResult *)biometricsObject error:(NSError *)error {
  if(biometricsObject != nil) {
    NSString *result = [NSString stringWithFormat:@"Biometrics object payload: %@ | images: %@", [biometricsObject payload], [biometricsObject images]];
    
    mResolve(result);
  } else {
    mReject(@"Biometrics Error", [error localizedDescription], error);
  }
}

Para retornar o resultado para o app React Native, escolhemos criar uma nova variável para armazenar o promise e chamamos essa variável no retorno do delegate.

📘

TIP

Para informações de como implementar a Biometria Facial com provider Facetec, veja as seções 4.3. Telas de Captura FaceTec e providerError na seção 5. Implementação do AllowMeBiometricsDelegate.

3. Integrando com o código TypeScript

Agora que criamos os canais de comunicação no Android e iOS precisamos chamá-los através da nossa aplicação React Native em TypeScript. Para isso vamos acessar o módulo nativo invocando seu método exportado em TypeScript. Fazemos isso da seguinte maneira:

import { NativeModules } from 'react-native';

const {AllowMeBridge} = NativeModules;

export default AllowMeBridge;

Por questões de organização de código, nós colocamos esse trecho isolado em um arquivo chamado AllowMeBridgeNativeModule.tx. Agora basta escolher onde você deseja utilizar o AllowMeSDK e importar o módulo:

import AllowMeBridge from './AllowMeBridgeNativeModule';

Para cada botão do nosso aplicativo React Native, vamos chamar cada uma das funções abaixo conforme a necessidade dos produtos contratados e implementados anteriormente

import {...};
import AllowMeBridge from './AllowMeBridgeNativeModule';

type State = {
  result: any;
}

export default class App extends React.Component<{}, State> {
  constructor(props: {}) {
    super(props);
    this.state = { result: '' };
  }

  // AllowMe Geral - Primeiros Passos
  async onSetupClick() {
    try {
      var result = await AllowMeBridge.initSetup();
      this.setState({ result: result });
    } catch (e) {
      console.error(e);
    }
  }

  // AllowMe Device Intelligence (Contextual)
  async onCollectClick() {
    try {
      var result = await AllowMeBridge.collect();
      this.setState({ result: 'Coleta: ' + result });
    } catch (e) {
      console.error(e);
    }
  }

  // AllowMe Validação de Endereço
  async onAddPersonClick() {
    try {
      var result = await AllowMeBridge.addPerson();
      this.setState({ result: result });
    } catch (e) {
      console.error(e);
    }
  }

  async onStartClick() {
    try {
      var result = await AllowMeBridge.start();
      this.setState({ result: result });
    } catch (e) {
      console.error(e);
    }
  }

  // AllowMe Biometria Facial
  onBiometricsClick() {
    AllowMeBridge.showBiometrics()
      .then((data: any) => {
        this.setState({ result: data });
      })
      .catch((e: any) => {
        console.error(e);
      });
  }
}

📘

TIP

Em caso dúvidas ou para ter acesso ao código fonte desse exemplo entre em contato com [email protected].