Fortface SDK integration in your iOS app

How does it work?

The purpose of our SDK is to capture the photo and send it to our Fortface API. To do this, there is a UIViewController that acts independently of your app, started by an Engine the moment it is established and, after processing, closes automatically.

Our SDK does not communicate directly with the Fortface API, see below for the communication flow:

Communication flow

Below are the instructions for integrating our SDK, the process flow for correctly accessing our service and step-by-step instructions on how to build an Engine.

Installation

Requirement

iOS: minimum supported version is 13.0

Installation

Versions starting from 1.8

1. Import folder via local SPM

Download the file by clicking the link below and unzip it: Fortface.xcframework-1.9.zip

  1. In your project, go to Package Dependencies and click the + at the end of the Packages list.
  2. A new screen to add packages will open. In the bottom-right corner, click +.
  3. At the bottom of the screen, click Add local and select the folder you downloaded earlier.
  4. Select your project target and click Add Package.
  5. The new dependency should appear on the right side of your Xcode under Package Dependencies.

Versions prior to 1.8

1. Add the file to your project

First you need to add the Fortface.framework to your project, download it by clicking on the following link and unzip the file: Fortface.xcframework-1.9.zip.

To include the Fortface SDK dependency in your app, simply add the sdk.framework folder to your project.

Example:

your_project/Fortface.framework

ADD PRINT OF FOLDERS HERE

Permission

In order to capture the photo, our SDK needs access to the device's camera. For this reason, permission is requested on first access.

Here are the steps:

  1. In Xcode, in the project browser tab (on the left), click on the project icon (blue) to select it;
  2. In the main project tab, in the “TARGETS” section, select your application;
  3. Info” tab, ‘Custom iOS Target Properties’ section, in the last Key click on the plus sign (+) to add a new property;
  4. Select “Privacy - Camera Usage Description”;
  5. In the “Value” field, enter a message explaining why the app needs access to the camera. For example: “You need to authorize access to the camera to capture photos and videos.”

Example:

permission

Pre-initialization

Before starting the capture process, it is necessary to establish the integrity of the device to ensure secure communication with the Fortface API. You therefore need to:

  1. import our SDK into your app;
  2. Instantiate FortfaceSDK.

Establish device integrity

Start the SDK and get the 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

In FortfaceSDK, start method will be used which has 1 parameter and returns 1 string:

  • Parameter isDebugMode: boolean that enables/disables debug mode. Default value false;
  • Use true to enable debug mode;
  • Use false in production, so that the integrity of the device is checked and, if it is not suitable for transactions, an exception of type FortfaceException will be thrown with the message There is something wrong!.
  • Return stored in deviceRequestInfo: string used to validate the integrity of the device connected to our SDK. A new one is generated each time the method is called (in the code above is an example).

With deviceRequestInfo in hand, you can now make a handshake call between your back-end and API Fortface.

handshake is an important security measure for establishing reliable communication between your back-end and API Fortface, making it possible to verify device authenticity, share sensitive information (encryption keys and sessions) and establish a secure channel for data exchange.

A successful handshake ensures that the device is intact and your app has the information it needs to proceed.

Capture

Knowing that the integrity of the device has been established, let's understand how to start the session and use the IFortfaceEngine interface to perform the capture.

Establishing the device session

Implement a class that extends IFortfaceEngine.

class Engine: IFortfaceEngine {

init(sessionId: String, sessionKey: String) {
FortfaceManagerSession.start(
engine: self,
sessionId: sessionId,
sessionKey: sessionKey,
orientationManager: FortfaceOrientationManager()
)
}

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]
// Here the flow ends and this is the data to send to the Fortface API
}

private func canceled() {
// Here the user has canceled the capture. You should continue with your cancellation flow
}

private func timeout() {
// Here the capture was canceled because it ran out of time. You must follow the timeout flow
}

private func cameraNotAuthorized() {
// Here the camera permission was not granted by the user. You must follow the camera permission flow
}

private func handleErrorAction(sessionDetails: Data) {
// Here we receive a JSON with the type of error that occurred.
}
}

In this class, initialize FortfaceManagerSession and its start method which has 3 parameters:

  • sessionId: session id, generated in the Fortface API;
  • sessionKey: encryption key generated in API Fortface;
  • orientationManager: the class responsible for knowing the orientation of the device;
  • getGeolocation: Define whether the SDK should collect location coordinates for anti-fraud reinforcement. (optional)
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")
}
}

Mock data for example:

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”

Handling the sessionKey

The string that represents the sessionKey contains special characters (such as the line break \n) that must be preserved exactly as received from the Fortface API. Be careful with any accidental manipulation that may occur in:

  • Form inputs
  • Logs and monitoring systems
  • String processing in your backend
  • Transfers between services
  • Database storage

Any change to these characters may cause the key validation to fail. Make sure the key is passed to the SDK exactly as received from the Fortface API.

Development facilitation

For local frontend development only, you may reuse the same valid sessionKey as a mock as many times as necessary. The single-use restriction applies only in the context of backend development, when the Fortface API validates the key.

Understanding the code snippet:

  • init will make the call to FortfaceManagerSession.start;
  • The session is created and the camera is rendered on the screen to capture the face of the user;
  • The data is encrypted and sent along with the captured photo to API Fortface;
  • The return occurs in the finishedSessionEngine function, which is a callback;
  • sessionEngineResult is the object that has 3 properties: data, action and sessionDetails.
  • If sessionEngineResult.action returns the value capture it is because the flow went as expected and returned the capture data;
  • data contains encrypted data and can be converted into JSON to make it easier to manipulate. The 4 keys are generated by the SDK, sent to your back-end and then to the Fortface API.
{
key: String,
data: String,
imgData: String,
imgPreview: String
}
  • If sessionEngineResult.action returns the value cancel, it's because the user canceled the capture by tapping the Back button, so follow the cancellation flow;

  • It is possible to hide the Back button and, consequently, not have action cancel, more details in the Customization topic.

  • If sessionEngineResult.action has a value of timeout, it means that the time set for capture has elapsed, so you should follow the timeout flow.

  • This action represents the amount of time that the SDK is open, whether it is capturing or not. When it runs out, the SDK is closed and the camera is closed;

  • The default time is 30 seconds:

  • If you want to keep it, you don't need to do anything;

  • If you want to change it, just customize the value in seconds, more details in the Customization topic.

  • If sessionEngineResult.action has a value of deniedCamera, it means that the user did not allow access to the camera when the permission alert appeared. You should therefore follow the camera permission flow.

  • If sessionEngineResult.action has a value of error, it means that an error occurred during document capture or upload. You should follow the error flow.

    • The type of error is returned in the sessionDetails JSON which can be decoded using the SessionDetailsModel. Currently, the errors received can be:
      • documentNotFound: code returned when the selected file (PDF/Image) cannot be found.
      • fileSizeTooBig: code returned when the selected file (PDF/Image) is larger than the allowed limit of 4MB.
      • fileCorrupted: code returned when the selected file (PDF/Image) is corrupted.

Versioning

To find out which version of our SDK is being used, simply call the function FortfaceSDK.getVersion() which will return a String with its value. Example:

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

Full screen mode

By default, our SDK is displayed in full screen mode in portrait orientation. This setting is valid for all supported devices. However, on tablets, it is possible to set the orientation to horizontal (landscape) or automatic, allowing the SDK to follow the orientation of the device in use.

On iOS, it is necessary to add this setting to the AppDelegate of the integrator application so that the orientationManager can adapt in cases of screen rotation.

import UIKit

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

If the SDK is configured for an orientation other than portrait and run on a smartphone, it will automatically be displayed in portrait, ignoring the customization made. The possible configuration values are:

customizer.screenMode.mode = .modal
customizer.cameraBackground.color = .red // modal da tela de câmera
customizer.instructionsBackground.color = .white // modal da tela de instruções

We offer the option of customizing the display of the SDK's capture stream in a modal that overlays the client application's screen. This functionality is only available for tablets. When configured and run on smartphones, the SDK will adopt full-screen display behavior in portrait orientation. In modal mode, you can customize the background color.

customizer.screenMode.mode = .modal
customizer.cameraBackground.color = .red // camera screen modal
customizer.instructionsBackground.color = .white // instructions screen modal

Cancel callback and timeout

When an attempt to capture the user's face is not completed successfully, for example when the user cancels the capture or when the capture doesn't take place during the specified time, it is important that the event is sent to the backend so that we have the correct metrics for the success or failure of the captures.

To allow us to have the data of the canceled or timeout capture, our SDK has callbacks in the cancel capture action and in the capture timeout action.

The SessionEngineResult received in the finishedSessionEngine function is an object in the Data() format which has 2 properties: date and action (action performed in the SDK). Previously, the date field was only filled in at the end of the face capture.

As seen above in Capture, the data contains encrypted data and can be converted into JSON. However, unlike the capture, in this scenario after conversion to JSON, we will only have the following fields:

{
key: String,
data: String
}

Reminder: The key and data keys are generated by the SDK, sent to your back-end and then to the Fortface API.

Implementation example

  1. we have identified in the SDK that there has been a capture cancellation.

  2. We create the encrypted object.

  3. Finish the SDK and send the generated payload and the action taken.

  4. In the implementation app, within the class that extends the IFortfaceEngine, it is detected that an action has taken place.

class Engine: IFortfaceEngine {

...

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

// when the cancel action is detected, the canceled function is called
// which receives the date generated in the SDK.
case .cancel:
canceled(data: sessionEngineResult.data ?? Data())

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

default: return
}
}

private func canceled(data: Data) {
// send the cancellation payload to a metrics API
metricService(data: Data)
}

private func metricService(data: Data) {
// we take the object of type Data and serialize it in JSON
if let metricRequest = try? JSONSerialization.jsonObject(with: self.actionServiceData, options: [])
as? [String: String] {

// transforms into an object of type Metric
let request = getMetricRequest(sdkJsonDataRequest: metricRequest)

// calls the Service layer, sending the Metric to the 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. Metric object sent to the API
import Foundation

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

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

Summary

  1. Check minimum requirements, add folder and authorize access to camera;
  2. Capture deviceRequestInfo;
  3. Do handshake;
  4. Start Engine;
  5. Receive data in callback;
  6. Send to API Fortface;
  7. Receive response from API Fortface and proceed with your business rule.