Page: FFL for IOS introduction
2021-10-14 22:10
-
Findface Liveness iOS SDK
- Getting started
- Installation
- Basic usage
- Further steps
- KVStorage
- FFLOnboarding
- FFLIdentification
- FFLVerification
- Annex 1 — CameraCapture class
Findface Liveness iOS SDK
Getting started
Requirements
- iOS 13.2 or later
- latest XCode version (to upload the app to the AppStore)
Resources
Installation
This framework could be installed by adding LivenessAPI.framework
to the desired project.
Basic usage
After installing the framework either via cocoapods or just cloning the sourcecode to your workspace, first you'd import the SDK:
import LivenessAPI
Importing the framework gives you an access to FFLApi
class, which is the main class that is used to interact with FindFace Liveness API and the one, that exposes all methods, types and properties available at the moment, as well as to FFLOnboarding
, FFLIdentification
and FFLVerification
classes, that implement common tasks using the abovementioned FFLApi
.
All those classes (and other useful objects) are described in SDK API Docs. Also you can find some of the main usecases further below.
Initialization
First of all you should initialize the api object as follows:
let api = FFLApi(storage: <KVStorage>,
rootUrl: <String>,
login: <String>,
password: <String>)
storage: KVStorage
— storage object conforming to KVStorage protocol
rootUrl: String
— String object with the API server url
login: String
— server login
password: String
— server password
Those are the main parameters, there are some other ones that could be specified, visit SDK API Docs and view XCode quick help documentation for further information.
The API instance uses FFLApiToken
from the storage specified, if any saved token is available. Otherwise it performs login using specified credentials.
To use another login and password, it is necessary to create another FFLApi instance, this is crucial for onboarding, verification and identification to work properly.
This SDK could work with the proxy server that forwards and processes calls to the Liveness API server. In order to do this, you should specify the proxy server
rootUrl
instead. The proxy server API used should be compatible/identical with the Liveness API server.
Unlike Android library version, the FFLApi instance doesn't connect on initialization, you should call
checkToken
method explicitly to connect.
checkToken
checkToken
method is used to check saved token (if any) validity and perform login procedure using login and password to get new token if needed.
api.checkToken({ (result) in
switch result {
case .success(let user):
// either succesfully got token from the API server, or the stored token is still valid
case .failure(let error):
// error occured
}
})
Further steps
After performing all the basic setup described above, you are ready to use all FFLApi
methods directly according to the desired business logic. However, there are additional helper classes that incapsulate some common required logic, as described below.
KVStorage
Storage is used by the SDK to store API token. It uses the key "FFLApiToken"
internally to save and retrieve the saved token.
You could use any other key to store your data (for example liveness-related) if you'd like to do so.
You could use any storage of your preference that conforms to KVStorage
protocol. Here is the basic implementation of the class that conforms to KVStorage
protocol, using UserDefaults
as an actual storage.
class UserDefaultsStorage: KVStorage {
func string(forKey: String) -> String? {
UserDefaults().string(forKey: forKey)
}
func setValue(value: String, forKey: String) {
UserDefaults().setValue(value, forKey: forKey)
UserDefaults().synchronize()
}
func removeObject(forKey: String) {
UserDefaults().removeObject(forKey: forKey)
}
}
FFLOnboarding
This is the first of three helper classes, that implements basic user onboarding flow. It's purpose is to create a user dossier (in terms of Findface Platform).
Onboarding is performed in the following steps:
- init — create instance
- addDocument — add document photo (from jpeg file)
- addSelfieMovie — add selfie movie (from mp4 file)
- createDossier — create a dossied by selecting unused login.
On every step application should repeat the step until succeed, re-taking photos, movies or asking user to select another login instead of already used.
Note that if some of these steps, namely addDocument, addSelfieMovie and createDossier, fail — it is ok to repeat any of them until successed. It is not necessary to repeat the whole sequence, for example, if the login has already been performed, just call createDossier one more time.
Initialization
let onboarding = FFLOnboarding(api: <FFLApi>,
threshold: <Double>)
api: FFLApi
— FFLApi
instance. Usually it would be the shared instance for the application.
threshold: Double
— tolerance parameter to use to check document and face similarity during the onboarding process. As for example it is 0.7
by default in the demo app.
addDocument
For this and addSelfieMovie
step you would need to be able to take a photo and a video using the phone camera. It could be achieved in a number of ways. For the sake of completeness of the flow, CameraCapture class object could be used. It is part of the demo application. It's usage would be described in the Annex 1 — CameraCapture class.
Generally speaking, photo and video size and format required may depend on the server configuration, there are some recommendations:
AVCaptureSession session.sessionPreset = .hd1280x720
— should work fine for both photo and videoAVCaptureMovieFileOutput movieOutput.setOutputSettings([AVVideoCodecKey: AVVideoCodecType.h264], for: connection!)
—h264
sould be fine by defaultThere is a default implementation example in the Annex 1.
This is the first onboarding step.
Having initialized onboarding
as a FFLOnboarding
instance, you could use the following code to add a document:
onboarding.addDocument(fileData: fileData) { [unowned self] (result) in
switch result {
case .Ok:
...
case .faceExists:
...
case ...:
...
default:
...
}
}
fileData
— image Data object, you get from the camera.
result
— callback closure parameter, result. You process possible outcomes, for example, in the switch statement, having .Ok
as a succesful outcome and processing other possible options as needed.
addSelfieMovie
The second onboarding step. Having initialized onboarding
as a FFLOnboarding
instance, you could use the following code to add a selfie movie:
onboarding.addSelfieMovie(videoFile: videoFileURL) { (result) in
switch result {
case .Ok:
...
case .differentFaces:
...
case .badVideo(code: .NOT_LIVE):
...
default:
...
}
}
videoFile
— video file local URL
. Generally you take a selfie video and store the video locally in a temporary file. This is the URL
pointing to that locally stored video file.
result
— callback closure parameter, result. You process possible outcomes, for example, in the switch statement, having .Ok
as a succesful outcome and processing other possible options as needed.
createDossier
The third onboarding step. Having initialized onboarding
as a FFLOnboarding
instance, you could use the following code to create a user dossier:
onboarding.createDossier(login: login, password: password) { (result) in
switch result {
case .Ok:
...
case .loginExists:
...
default:
...
}
}
login
— as the name implies, the desired user login.
password
— as the name implies, the desired user password.
result
— callback closure parameter, result. You process possible outcomes, for example, in the switch statement, having .Ok
as a succesful outcome and processing other possible options as needed.
After this step completes succesfully, the onboarding flow is finished.
There is a demo application for further details and flow example.
Onboarding result
Possible onboarding results are the following:
public enum OnboardingResult {
case Ok
case badVideo(code: FFLApi.LivenessResult)
case lowPhotoQuality
case differentFaces
case faceExists
case loginExists
case networkError(error: FFLApi.NetworkError)
case error(error: Error)
case unexpectedError(code: String = "unknown", message: String = "unknown error")
}
Ok
means successful completion of the steplowPhotoQuality
document photo is too badbadVideo
the selfie video is bad. The reason for the error is described with the code provided.differentFaces
the face found on the selfie video does not resemble the one on the documentfaceExists
the face is already associated to some profileloginExists
the login name is already in usenetworkError
network error occured, error details are availableerror
SDK error occured, error details are availableunexpectedError
for any other error that normally should not happen
badVideo code
Code that represents the result of liveness check, with a video quality error report.
enum LivenessResult: Int {
case OK
case NO_FACE
case SEVERAL_FACES
case FACE_TOO_BIG
case FACE_TOO_SMALL
case FACE_CLOSE_TO_BORDER
case FACE_OVEREXPOSED
case FACE_UNDEREXPOSED
case NOT_LIVE
case UNKNOWN_ERROR
}
Those cases correspond to the ones described in the FindFace Liveness server documentation.
OK
— Everything is ok, live face has been detected.NO_FACE
— live face was not foundSEVERAL_FACES
— there are more than one face in the videoFACE_TOO_BIG
— face is too big, camera might be too closeFACE_TOO_SMALL
— face is too small, camera might be too farFACE_CLOSE_TO_BORDER
— face in the movie is too close to the border of the frameFACE_OVEREXPOSED
— too many light to process the frameFACE_UNDEREXPOSED
— light is too low to process the frameNOT_LIVE
— the face detected is not liveUNKNOWN_ERROR
— any other unclassified error
FFLIdentification
Identification instance for a given API.
Identification is a process of finding a dossier by checking and scanning live selfie movie against the dossier available on fflsecurity server. See perform for details.
Identification is performed in the following steps:
- init — create instance
- perform — perform the user identification
Initialization
let identification = FFLIdentification(api: <FFLApi>)
api: FFLApi
— FFLApi
instance. Usually it would be the shared instance for the application.
perform
Looks for the Dossier
associated with a live face presented in the selfie file after ensuring it is a live person video. See FFLApi.LiveIdentificationResult
for explanation of resulting values. The method could be safely called multiple times on the same instance.
identification.perform(videoFile: videoFileURL) { (result) in
switch result {
case .success(let dossier):
...
default:
...
}
}
videoFile
— video file local URL
. Generally you take a selfie video and store the video locally in a temporary file. This is the URL
pointing to that locally stored video file.
After succesful identification, result
success
case contains Dossier
instance that has been found.
FFLVerification
Verification is a process of checking that the login (and optionally also a password) matches the live person face selfie movie.
Verification is performed in the following steps:
- init — create an instance with the API object
- login — login with valid credentials
- verify — verify a freshly captured selfie
Initialization
let verification = FFLVerification(api: <FFLApi>,
threshold: <Double>)
api: FFLApi
— FFLApi
instance. Usually it would be the shared instance for the application.
threshold: Double
— tolerance parameter to use to check stored and live face similarity during the verification process. As for example it is 0.7
by default in the demo app.
login
Login, checking the credentials provided. Be sure to get a non-nil dossier. It will check if the login is ok, and, optionally, the password. Repeat until the process succeeds.
verification.login(login: login, password: password, { (dossier) in
if dossier == nil {
// wrong login or password
} else {
// success
}
})
login
— as the name implies, the user login.
password
— as the name implies, the user password.
dossier
— if dossier
is not nil, the user with login
and password
specified exists. dossier
is set to the user's Dossier
. Otherwise, credentials were wrong, or user doesn't exist.
verify
Verify a freshly captured selfie and process its result. It checks that the video is live and contains an image of the person that matches documents registered with the dossier. Repeat the process if needed.
verification.verify(videoFile: videoFileURL) { (result) in
switch result {
case .success(let dossier):
...
default:
...
}
}
})
videoFile
— video file local URL
. Generally you take a selfie video and store the video locally in a temporary file. This is the URL
pointing to that locally stored video file.
After succesful verification, result
success
case contains Dossier
instance that has been verified.
Annex 1 — CameraCapture class
You may find a CameraCapture
class in the demo application or it may be provided with the SDK distribution. It encapsulates the main photo and video capturing API, providing two main methods makePhoto
and takeVideo
.
Basically the class is self-explaining and describing ways of capturing photos and videos in iOS is not an intended goal of this document, so the following is a quick how-to.
First, you initialize CameraCapture
instance:
var cameraCapture = CameraCapture(containerView: cameraContainerView, cameraPosition: .front)
here cameraContainerView
is a UIView in the view hierarchy that is used as a camera preview, and cameraPosition indicates which camera to use (front or back one).
Then you start and stop camera preview in viewWillAppear
and viewWillDisappear
respectively:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
cameraCapture?.startCamera()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
cameraCapture?.stopCamera()
}
To correctly support interface changes (rotation for example) you override viewDidLayoutSubviews
:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
cameraCapture?.setupPreviewFrame()
}
Then you could capture photo or movie when you are ready:
cameraCapture?.makePhoto { (imageData) in ... }
cameraCapture?.takeVideo(file: filename, { (file) in ... })
filename
here is the local URL
where you desire to store taken movie file.
Then you process taken photo or video as needed inside the callback closure.