Integração Fortface SDK no seu app iOS

Como funciona?

Objetivo da nossa SDK é efetuar a captura ou upload da foto do documento e enviá-la para nossa API Fortface. Para isso, há uma UIViewController que atua de forma independente do seu app, iniciada por uma Engine no momento que for estabelecido e, após processamento, encerra automaticamente.

Nossa SDK não se comunica diretamente com a API Fortface, veja abaixo o fluxo de comunicação:

Fluxo de comunicação

Abaixo, seguem as instruções de integração da nossa SDK, fluxo de processos para acessar corretamente nosso serviço e passo-a-passo de como construir uma Engine.

Instalação

Requisito

iOS: versão mínima suportada é 13.0

Instalação

Versões a partir da 1.8

1. Importar pasta via SPM local

Faça o download clicando no link a seguir e descompacte o arquivo: Fortface.xcframework-1.9.zip

  1. No seu projeto, vá em Package Dependencies e clique no + no final da listagem de Packages.
  2. Em seguida, irá abrir uma nova tela para adicionar pacotes. No canto direito inferior, clique em +.
  3. Na parte inferior da tela, clique em Add local e selecione a pasta baixada anteriormente.
  4. Selecione o target do seu projeto e clique em Add Package.
  5. A nova dependência deve aparece na lateral direita do seu Xcode na parte de Package Dependencies.

Versões anteriores a 1.8

1. Adicionar o arquivo ao seu projeto

Primeiramente é necessário adicionar o Fortface.framework ao seu projeto, faça o download clicando no link a seguir e descompacte o arquivo: Fortface.xcframework-1.9.zip.

Para incluir a dependência da Fortface SDK no seu app, basta adicionar a pasta sdk.framework no seu projeto.

Exemplo:

seu_projeto/Fortface.framework

ADICIONAR PRINT DAS PASTAS AQUI

Permissão

Para efetuar a captura da foto, nossa SDK precisa ter acesso à câmera do dispositivo. Por isso, é solicitada permissão já no primeiro acesso.

Seguem os passos:

  1. No Xcode, guia do navegador de projetos (à esquerda), clique no ícone do projeto (azul) para selecioná-lo;
  2. Na guia principal do projeto, na seção "TARGETS" (ALVOS), selecione seu aplicativo;
  3. Guia "Info" (Informações), seção "Custom iOS Target Properties" (Propriedades Personalizadas do Alvo iOS), na última Key (Chave) clique no sinal de adição (+) para acrescentar uma nova propriedade;
  4. Selecione "Privacy - Camera Usage Description" (Privacidade - Descrição do Uso da Câmera);
  5. No campo "Value" (Valor), insira uma mensagem explicando porquê o app precisa acessar câmera. Por exemplo: "É necessário autorizar acesso à câmera para capturar fotos e vídeos."

Exemplo:

permissão

Pré-inicialização

Antes de iniciar o processo de captura, é necessário estabelecer a integridade do dispositivo para garantir uma comunicação segura com a API Fortface. Sendo assim, é preciso:

  1. Importar nossa SDK no seu app;
  2. Instanciar FortfaceSDK.

Estabelecer a integridade do dispositivo

Inicie a SDK e obtenha o deviceRequestInfo.

import Fortface

let deviceRequestInfo = FortfaceSDK.start(isDebugMode: true)
print(deviceRequestInfo)
// ztdTVLuGyQegvUnEtwAzcHbK8RoA9CXTuNRpCi9YXcwlzWpzOff+iOvUMkdprLk1E3npSKmk868x4pdJELztvRwu0auBrQdVFTu4wUI8ne35blqq1fds0ZHkOx8pfZGD4SIIPEVTEg2+uXQ8k6lxg7xoSnGzIPQJBZJmQWyDECXKDlXt+AAzMGN4lIWB2uM3ncwQLHPGDcGx52EgXBt3yQV7xZrCdinlwlDb21GK1kjygkRixqCvcB3LudlD8bYKKZq1PO5+faL0gFp6cTWpU9Fqkf7l9kbMc/5n+RGE83QzcQuLBzphDmIX2EUsqIdKFk/eg8WqAA9iC9YX4271/gt6RXqy2oQVJ4cyqD6bpLcRZtk/kLcW1lbo1h4bWyTyjC3XXnRPZVComA51s3Ouzxm4IxKX/EaTMd88n/ABiuE2s2DDKKKYc+WbqrDJo1btFHuXDA+O66mjrNKP+VE/CgtOrWF6W4x2cnpTNBJALmX23tion0caYxzbYpC18i3U

No FortfaceSDK, será usado método start que possui 1 parâmetro e retorna 1 string:

  • Parâmetro isDebugMode: booleano que habilita/desabilita modo de depuração. Valor default false;
    • Use true para habilitar o modo de depuração;
    • Use false em produção, assim é verificado a integridade do dispositivo e, caso não seja adequado para transações, será lançada uma excepetion do tipo FortfaceException com a mensagem There is something wrong!.
  • Retorno armazenado em deviceRequestInfo: string usada para validar integridade do dispositivo conectado à nossa SDK. Uma nova é gerada cada vez que o método é chamado (no código acima está um exemplo).

Com deviceRequestInfo em mãos, agora é possível fazer uma chamada de handshake entre seu back-end e API Fortface.

handshake é uma medida de segurança importante para estabelecer uma comunicação confiável entre seu back-end e a API Fortface, possibilitando verificar autenticidade do dispositivo, compartilhar informações sensíveis (chaves de criptografia e sessões) e estabelecer um canal seguro para troca de dados.

Com o sucesso do handshake, haverá a garantia de que o dispositivo está íntegro e seu app tem as informações necessárias para prosseguir.

Captura e upload de documentos

Limite de Tamanho

O tamanho máximo de um arquivo (PDF/Imagem) para upload é de 4MB. Arquivos maiores serão rejeitados com o código de erro FileSizeTooBig.

Sabendo que a integridade do dispositivo foi estabelecida, vamos entender como iniciar a sessão e utilizar a interface IFortfaceEngine para realizar a captura.

Estabelecer a sessão do dispositivo

Implemente uma classe que extenda IFortfaceEngine.

class Engine: IFortfaceEngine {

init(sessionId: String, sessionKey: String, previousDoc: DocumentTypeEnum, documentList: [DocumentTypeEnum], openPhotoGalery: Bool) {
// Para captura de documentos
FortfaceManagerSession.startDocSessionEngine(
engine: self,
sessionId: sessionId,
sessionKey: sessionKey,
documentTypes: documentList,
previousDocType: previousDoc,
orientationManager: FortfaceOrientationManager()
)

// Para upload de documentos
FortfaceManagerSession.startUploadDocSessionEngine(
engine: self,
sessionId: sessionId,
sessionKey: encryptKey,
documentTypes: documentTypes,
openPhotoGalery: openPhotoGalery,
previousDocType: previousDoc
)
}

func finishedSessionEngine(sessionEngineResult: SessionEngineResult) {
switch sessionEngineResult {
case .capture:
captured(data: sessionEngineResult.data ?? Data())

case .cancel:
canceled()

case .timeout:
timeout()

case .deniedCamera:
cameraNotAuthorized()

case .error:
handleErrorAction(sessionDetails: sessionEngineResult.sessionDetails ?? Data())

default: return
}
}

private func captured(data: Data) {
let sdkData = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: String]
// Aqui encerra o fluxo e esses são os dados para enviar à API Fortface
}

private func canceled() {
// Aqui a pessoa usuária cancelou a captura. Você deve seguir com seu fluxo de cancelamento
}

private func timeout() {
// Aqui a captura foi cancelada pois o tempo esgotou. Você deve seguir com fluxo de tempo limite esgotado
}

private func cameraNotAuthorized() {
// Aqui a permissão de câmera não foi concedida pelo usuário. Você deve seguir com fluxo de permissão de câmera
}

private func handleErrorAction(sessionDetails: Data) {
// Aqui recebemos um JSON com o tipo de erro que aconteceu.
}
}

Nesta classe, inicialize FortfaceManagerSession e seu método startDocSessionEngine que possui 5 ou 6 parâmetros:

  • engine: representa a sua classe Engine que implementa a interfaceIFortfaceEngine da nossa SDK. Isso será explicado logo abaixo, na sessão Criando um Engine.
  • sessionId: id da sessão, gerado na API Fortface;
  • sessionKey: chave de criptografia gerado na API Fortface;
  • documentTypes: representa a lista de documentos aceitos.
    • Os documentos aceitos são do tipo DocumentTypeEnum e podem ser: RG_FRONT, CNH_FRONT, RG_BACK, CNH_BACK, UNDEFINED, DOC_NOT_FOUND, PASSPORT.
  • previousDocType: representa o tipo de documento capturado anteriormente. Quando se é capturado / feito upload de um documento na primeira vez, esse parâmetro deve ter valor nil. O valor desse parâmetro é obtido através do response do serviço de API Fortface apoós o envio da primeira face.
  • openPhotoGalery: usado apenas no upload de documento. Recebe um Bool que indica se o upload de documentos será via galeria de fotos os galeria de arquivos.
  • orientationManager: é a classe responsável por saber a orientação do dispositivo;
  • getGeolocation: define se o SDK deve coletar coordenadas de localização para reforço antifraude. (opcional)

O método startDocSessionEngine, que atualmente recebe até seis parâmetros para a captura de documentos, será descontinuado. Esse método já está marcado como obsoleto (@Deprecated) e deixará de funcionar em futuras versões do SDK. Os parâmetros previousDocType e documentTypes serão descontinuados nas próximas releases.

class FortfaceOrientationManager: IFortfaceOrientationManager {
private func lockOrientation(_ orientation: UIInterfaceOrientationMask) {
if let delegate = UIApplication.shared.delegate as? AppDelegate {
delegate.orientationLock = orientation
}
}

func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation: UIInterfaceOrientation) {
self.lockOrientation(orientation)
UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation")
}
}

É necessário adicionar esta configuração no AppDelegate do aplicativo integrador para que o orientationManager consiga se adequar nos casos de rotacionamento de tela. Atualmente estamos aceitando apenas a orientação portrait em captura de documentos.

import UIKit

class AppDelegate: UIResponder, UIApplicationDelegate {
(...)
var orientationLock = UIInterfaceOrientationMask.all
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
return self.orientationLock
}
}

Dados mockados para exemplo:

sessionId = "nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2k"

sessionKey = "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2k+o81Oiti4TDezZYD8Q\nzj2RjO5f/YQHGmC1v7CWC4AJpD9+FOXBBO1pImaSg1gb8kOobRFGWbVdiHm35+PF\nTJceH31r+jKnvfc1TshsQVGtYLm7x5E/GFcjpGx9LdSxiXC6cHdCJf3o02/lytBa\nu8NCoH9FpPjyKrdGdSRb2haviAuEV4N1UBd5uZIsacVqZj52i1z+I94gLVKBZ/UV\npaxjLPWWngmaxbyKE0sEbt9JGleiCb2P1kZA9sU4L4Po7WBNk0+QOsvW9QjvS1+6\ntcZwwIDll2LyUceiFgxF6GkuFcHlBZDDtzymDDC2IYrf9/FNBOPeV1JHXON8T6DY\nAwIDAQAB\n-----END PUBLIC KEY-----\n"

Manipulação da sessionKey

A string que representa a sessionKey contém caracteres especiais (como a quebra de linha \n) que devem ser preservados exatamente como recebidos da API Fortface. Tenha cuidado com manipulações acidentais que possam ocorrer em:

  • Inputs de formulários
  • Logs e sistemas de monitoramento
  • Processamento de strings no seu backend
  • Transferência entre serviços
  • Armazenamento em bancos de dados

Qualquer alteração nesses caracteres pode fazer com que a validação da chave falhe. Certifique-se de que a chave seja passada para o SDK exatamente como recebida da API Fortface.

Facilitação de desenvolvimento

Para desenvolvimento local do frontend exclusivamente, você pode reutilizar uma mesma sessionKey válida como mock quantas vezes forem necessárias. A restrição de uso único só é aplicada no contexto do desenvolvimento do backend, quando a API Fortface valida a chave.

Entendendo o trecho de código:

  • init vai fazer a chamada para FortfaceManagerSession.start;
  • A sessão é criada e a câmera é renderizada na tela para captura de documento da pessoa usuária;
    • Obs: no caso de upload de documento é aberto a galeria de arquivos ou galeria de fotos.
  • Os dados são criptografados e enviados junto com a foto capturada para API Fortface;
  • O retorno ocorre na função finishedSessionEngine que é callback;
  • sessionEngineResult é o objeto que possui 3 propriedades: data, action e sessionDetails
  • Se sessionEngineResult.action trouxer o valor de capture foi porque o fluxo seguiu como esperado, tendo retornado os dados da captura / upload;
  • data contém dado criptografado e pode ser convertido em JSON para facilitar sua manipulação. As chaves são geradas pela SDK, enviadas ao seu back-end e então à API Fortface.
{
key: String,
data: String,
imgData: String
}
  • Se sessionEngineResult.action trouxer o valor de cancel foi porque pessoa usuária cancelou a captura ao tocar no botão Voltar, dessa forma deve-se seguir fluxo de cancelamento;

    • É possível esconder botão Voltar e, por consequência, não ter action cancel, mais detalhes no tópico Customização.
  • Se sessionEngineResult.action trouxer o valor de timeout significa que o tempo estabelecido para captura foi esgotado, dessa forma deve-se seguir fluxo de tempo limite esgotado.

    • Essa action representa o tempo que a SDK fica aberta, realizando captura ou não. Quando esgotar, a SDK é encerrada e câmera fechada;
    • O tempo padrão é de 20 segundos:
      • Caso deseje manter, não precisa fazer nada;
      • Caso deseje alterar, basta customizar o valor em segundos, mais detalhes no tópico Customização.
  • Se sessionEngineResult.action trouxer o valor de deniedCamera significa que o usuário não permitiu o acesso a câmera quando o alerta de permissão apareceu. Dessa forma deve-se seguir fluxo de permissão de câmera.

  • Se sessionEngineResult.action trouxer o valor de error significa que ocorreu algum erro durante a captura ou upload de documento. Dessa forma deve-se seguir fluxo de erro.

    • O tipo de erro é retornado no JSON sessionDetails que pode ser decodificado usando o modelo SessionDetailsModel. Hoje os erros recebidos podem ser:
      • sameFace: código retornado quando a captura de uma segunda face do documento é do mesmo tipo que a face anterior informada.
      • documentNotFound: código retornado quando o arquivo selecionado(PDF/Imagem) não consegue de ser encontrado.
      • fileSizeTooBig: código retornado quando o arquivo selecionado(PDF/Imagem) é maior que o limite permitido de 4MB.
      • fileCorrupted: código retornado quando o arquivo selecionado(PDF/Imagem) esta corrompido.

ATENÇÃO!!

O fluxo é feito para cada “face” do documento, ou seja, frente e verso. Cada um deve ser feito o fluxo completo, passando pelo Handshake e startDocSessionEngine. Para a captura da segunda face deve ser informada o tipo de documento (Face do documento) reconhecimento na primeira captura, essa informação esta disponivel no response da chamada de API do serviço Fortface. A propriedade do json de response que se refere ao tipo de documento é a propriedade type, que esta contida no objeto fortfaceSdk.

Exemplo de json de response da API de serviço Fortface para captura de documento:

{
"api":{
"timestamp":1741712439604,
"version":"v1.13"
},
"handshake":{
"sessionId":"187ffa8d-016d-c0962506f5ab",
"sessionKey":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2t6V+LTwyClMN/2Dy5W8\nrJKMAwP45QTq+8e3RcNowUqKEDrsjSkBDYugYCaEuLY66pP3ndm8MfenHxuxAqGC\n3NagAUr83+kJufsizuUJTYC2/1CXeox8U1j4S0Ewt/4rr9SIZCwBisiFiT0THt6QFjE\nmwWMYVT4RjRUK8LV6pF1HJvjFHI6oXF2KjjFwVqXur5LB7sj9f70ddcp3Fiv/3Tn\nVQIDAQAB\n-----END PUBLIC KEY-----\n"
},
"message":"",
"providers":{
"fortfaceSdk":{
"automatic":true,
"confidence":0.73,
"type":"CNH_FRONT"
},
"identify":{
"status":"PENDING"
},
"ocr":{
"status":"PENDING",
"type":"ocr"
}
},
"sdk":{
"brand":"APPLE",
"model":"IPHONE_14",
"platform":"IOS"
},
"title":""
}

Versionamento

Para saber qual versão da nossa SDK está sendo utilizada, basta chamar a função FortfaceSDK.getVersion() que retornará uma String com seu valor. Exemplo:

let sdkVersion = FortfaceSDK.getVersion()
print(sdkVersion)
// 1.5.2

Callback de cancelamento e timeout

Quando uma tentativa de captura do rosto do usuário não é completada com sucesso, por exemplo quando o usuário cancela a captura ou quando a captura não acontece durante o tempo determinado, é importante que o acontecimento deste evento seja enviado ao backend para termos corretas métricas de sucesso ou falhas das capturas.

Para permitir que tenhamos os dados da captura cancelada ou que sofreu timeout, nossa SDK possui callbacks na ação de cancelar captura e na ação de timeout de captura.

O SessionEngineResult recebido na função finishedSessionEngine é um objeto no formato Data() que possui 2 propriedades: data e action (ação realizada na SDK). Anteriormente, o campo data era somente preenchido ao finalizar a captura de face.

Como visto anteriormente no tópico Captura, o data contém dados criptografados e pode ser convertidos em JSON. Entretanto, diferentemente da captura, neste cenário após a conversão para JSON, teremos somente os seguintes campos:

{
key: String,
data: String
}

Lembrete: As chaves key e data são geradas pela SDK, enviadas ao seu back-end e então à API Fortface.

Exemplo de implementação

  1. Identificamos na SDK que houve um cancelamento de captura.

  2. Criamos o objeto criptografado.

  3. Terminamos a SDK e enviamos o payload gerado e a ação realizada.

  4. No app de implementação, dentro da classe que extende o IFortfaceEngine, é detectado que houve uma ação.

class Engine: IFortfaceEngine {

...

func finishedSessionEngine(sessionEngineResult: SessionEngineResult) {
switch sessionEngineResult {
case .capture:
captured(data: sessionEngineResult.data ?? Data())

// ao detectar a ação de cancelar, é chamada a função canceled
// que recebe a data gerada na SDK.
case .cancel:
canceled(data: sessionEngineResult.data ?? Data())

case .timeout:
timeout(data: sessionEngineResult.data ?? Data())

case .error:
handleErrorAction(sessionDetails: sessionEngineResult.sessionDetails ?? Data())

default: return
}
}

private func canceled(data: Data) {
// envio do payload de cancelamento para uma API de métricas
metricService(data: Data)
}

private func metricService(data: Data) {
// pegamos o objeto do tipo Data e serializamos em JSON
if let metricRequest = try? JSONSerialization.jsonObject(with: self.actionServiceData, options: [])
as? [String: String] {

// transforma em um objeto do tipo Metric
let request = getMetricRequest(sdkJsonDataRequest: metricRequest)

// chama a camada de Service, enviando o Metric para a API
serviceAPI.postSessionMetric(metricRequest: request){ response, error in }
}
}

private func getMetricRequest(sdkJsonDataRequest: [String: String]) -> Metric {
let sdkData = SdkData(
key: sdkJsonDataRequest["key"], data: sdkJsonDataRequest["data"], imgData: nil)
return Metric(sdkData: sdkData)
}

...
}
  1. Objeto Metric enviado para a API
import Foundation

struct Metric: Codable {
let sdkData: SdkData?
}
  1. Objeto SdkData
import Foundation

struct SdkData: Codable {
let key: String?
let data: String?
let imgData: String?
}

Resumo

  1. Verificiar requisitos mínimos, acrescentar pasta e autorizar acesso à câmera;
  2. Capturar deviceRequestInfo;
  3. Fazer handshake;
  4. Iniciar Engine;
  5. Receber dados no callback;
  6. Enviar para API Fortface;
  7. Receber resposta da API Fortface e seguir com sua regra de negócio.