NAV Navbar
swift
  • Verifai iOS SDK (v 2.5.0)
  • Scan modes
  • Verifai NFC
  • NFC eMRTD Check
  • Verifai iOS SDK (v 2.5.0)

    Welcome to the Verifai App implementation documentation. If you have any questions that are unanswered you can always contact our support.

    This documentation wil continue to expand as we add extra services.

    Older versions

    If you're looking for documentation for previous versions of Verifai then check below:

    Version Link
    2.4.x Documentation
    2.3.x Documentation
    2.2.x Documentation
    2.1.x Documentation
    2.0.x Documentation
    1.1.x Documentation
    1.0.x Documentation

    Prerequisites

    Example project

    All the concepts documented so far have also been included in the example app that you can download to see a working example of Verifai.

    Example app on Github

    Installation

    There are multiple ways to install the Verifai SDK in your app. You can install automatically through CocoaPods or Carthage (recommended), or manually.

    Not only are there multiple installation methods, there are also two different distributions of the Verifai iOS SDK: Verifai and VerifaiLite. Verifai is the full package which includes all features offered by Verifai. VerifaiLite is a stripped down version, which doesn't include NFC capabilities. Installation for both versions is the same, except for some minor adjustments in your code.

    CocoaPods

    CocoaPods is a dependency manager for iOS projects.

    To add the full SDK through Cocoapods add the following line to your Podfile:
    pod 'Verifai', '~> 2.0'

    To add a version of the SDK that doesn't include the NFC capabilities of Verifai you can add the following line to your Podfile:
    pod 'VerifaiLite', '~> 2.0'

    Carthage

    Carthage is a simple, decentralized dependency manager.

    To add the SDK with NFC support through Carthage add the following line to your Cartfile:
    binary "https://dashboard.verifai.com/downloads/sdk_nfc/carthage.json" ~> 2.0

    To add the VerifaiLite SDK through Carthage add the following line to your Cartfile:
    binary "https://dashboard.verifai.com/downloads/sdk/carthage.json" ~> 2.0

    Manually

    Finally, it is also possible to manually install the framework.

    1. Download the Verifai/VerifaiLite framework along with the required support frameworks provided by the download from https://dashboard.verifai.com/downloads/sdk/
    2. Copy these frameworks in the desired framework folder in your project directory (e.g. $(PROJECT_DIR)/Frameworks)
    3. Add all these frameworks to the project in Project -> General -> Embedded Binaries.
    4. Make sure that the frameworks are also listed in Project -> General -> Linked Frameworks and Binaries and Project -> Build Phases -> Link Binary with Libraries. This usually happens automatically.
    5. Make sure that the framework folder is included in Project -> Build Settings -> Search Paths -> Framework Search Paths. For example: if the frameworks reside in the Project Root/Frameworks folder, add the entry $(PROJECT_DIR)/Frameworks to the Framework Search Paths.

    One final note on manually adding frameworks: when publishing your app to the App Store Apple will only accept an archive which contains iOS device binaries (so no simulator binaries). Since universal binaries are delivered (both simulator and device) the frameworks contain both architectures. As such, you may need to strip the provided framework binaries to get rid of the simulator architectures (x86 and i386). Dependency managers like CocoaPods and Carthage do this automatically for you. When debugging on your device Xcode also automatically does this for you, but it doesn't when Archiving. There are a multitude of tools and automatic scripts to find which fix this for you, but it is also possible to use the provided script from Carthage (/usr/local/bin/carthage copy-frameworks). Make sure to provide the proper paths to the framework binaries (e.g. $(PROJECT_DIR)/Frameworks) to the input files, resulting in e.g. $(PROJECT_DIR)/Frameworks/Verifai.framework, $(PROJECT_DIR)/Frameworks/ZIPFoundation.framework. Also make sure to put in the right output files to e.g. $(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/Verifai.framework, $(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/ZIPFoundation.framework.

    Authenticate App

    In this example we simply put the licence string in a global var, but it's up to you how you want to do it. You could just as easily load the licence string from an encrypted file. It's your responsibility to keep your licence safe inside of your app.

    let licenceString = """
                         === Verifai Licence file V2 ===
                         tsacCbEISbjEBPzcNjUrVnVA03E4tb...... the rest of your licence
                        """
    

    After adding the licence we can tell the SDK to use our licence.

    if let bundleIdentifier = Bundle.main.bundleIdentifier {
        Verifai.configure(licence: licenceString,
                          identifier: bundleIdentifier)
                        .success({ (_) in
                            // The operation succeeded
                        })
                        .error({ (error) in
                            // An error occurred
                        })
        }
    }
    

    After generating a licence on the dashboard you can choose your own way of getting it into the app. The licence can then be passed on to Verifai through the Verifai.configure() method. Verifai uses response blocks to communicate results, errors and progress after each operation. In the example on the side we implement the success and error blocks to give you an example on how to use them. More information about the VerifaiResponseBlock can be found later in this document.

    An example on how to get more info if a licence is not valid

    .error({ (latestError) in
        if latestError == VerifaiFatalError.licenceNotValid {
            // Handle licence error
            if let error = latestError as? VerifaiFatalError {
                    let licenceInfo = error.licenceInfo()
                    print("expirationDate: \(licenceInfo.expirationDate)")
                    print("identifier: \(licenceInfo.identifier)")
            }
        }
    })
    

    Errors

    Listen to the error block (like in the Verifai.configure() example) to handle the following VerifaiMessage value:

    If you receive this error enum you can call it's licenceInfo() enum function to get more details about the error.

    Scan modes

    AI mode

    When starting with ai mode Verifai will recognize documents automatically and cut the document from the image.

    Detailed info here: Scanning a document

    Manual Mode

    When starting with manual mode the user has to select the country, document type, model and the position of the document correctly on the screen.

    Detailed info here: Scanning a document

    Loading a local neural model

    You can download a Verifai package from the Verifai dashboard.

    Package Downloads

    To use the SDK without internet connectivity please ensure that your package is compiled along in your bundle. You can do this by adding the package file to your project and assuring that copy items if needed checkmark is selected.

    copyItems

    After you've added the file make sure in the File Inspector that there's a checkmark next to your target.

    fileInspector

    An example of this could be by loading the model in the init of your class. As long as the path is correct Verifai will be able to handle the parsing of the model and all its information. The VerifaiResponseBlock progress block can be used to track the progress of loading the model (int between 0-100).

    /// Load local document classifier model
    private func loadLocalModel() {
        // Set model in Verifai
        Verifai.setDocumentClassifierNeuralModel(modelPath: modelUrl)
        // Handle the completion
        .success({ (_) in
            // Model loading completed successfully
        })
        // Listen to progress updates
        .progress({ (progress) in
            print("⏱ Load model progress: \(progress)")
        })
        // Handle errors
        .error({ (error) in
            // An error occurred
            print("Error: error.description")
        })
    

    Verifai works with document classifier models in the package that allow the app to scan certain documents. These models have to be loaded to Verifai before they can be used. Verifai requires the use of the setDocumentClassifierNeuralModel(modelPath: URL?) or downloadDocumentClassifierNeuralModels(for countries: [String], automaticUpdate: Bool) function to load a model.

    Alternatively you can download a model or update your model package while the app is running, more on that below.

    It's your responsibility to provide Verifai with the correct path to the place in the bundle or user folder where the model is stored (i.e. set6_auto_ml.package).

    Downloading / Updating the document classifier neural model

    An example of this would be

    /// Load online model
    private func loadOnlineModel() {
        Verifai.downloadDocumentClassifierNeuralModels(for: ["NL"])
        .success(_) {
                // Successfully downloaded model
        }
        .error(error) {
            // An error occurred
            print("An error occurred: \(error.description)")
        }
    }
    

    Verifai also support updating or downloading neural model packages while the app is running. An active internet connection is needed. All you need to supply is the country codes for which to download the neural model. To get a list of supported countries call the downloadCountries() function. Once the model is loaded then the success block will be called. Catch any error in the error block. The progress block can be used to track the progress of the download.

    Next level OCR

    An example of this would be

        // Get latest OCR model
        Verifai.downloadOCRMRZNeuralModels()
        .success {
            print("Successfully downloaded ocr model")
        }.error({ (error) in
            print("An error occurred: \(error.description)")
        })
        // Listen to progress updates
        .progress({ (progress) in
            print("⏱ Load ocr model progress: \(progress)")
        })
    

    Since version 2.5.0 Verifai uses an advance OCR engine that uses neural networks to get the best possible OCR result when scanning documents. Before any MRZ OCR functions can be performed you need to have either set a local OCR MRZ network available here or download the latest one with the downloadOCRMRZNeuralModels function.
    If the OCR model has not been loaded by the time it is needed a VerifaiAiError.ocrNeuralModelNotLoaded will be returned.

    Errors

    The completed VerifaiResponseBlock for the neural model downloads can have one of the following VerifaiMessage values:

    Scanning a document

    Scanning a document is a straightforward process. The SDK was designed to be as flexible as possible while taking the least amount of arguments possible. This requires that the developer prepares the data that gets sent to the Verifai SDK scanning process.
    Verifai supports two modes when scanning documents. An ai mode which tries to find the right document automatically by looking through the documents in the neural model. And manual mode in which Verifai lets the user choose the document that needs to be scanned before showing the camera.

    VerifaiScanMode

    Property Description
    ai (BETA) Automatically try to detect the document and it's position (Default)
    manual Mode where the user first chooses the document that needs to be scanned from a menu and then places the document before the camera.

    The ai scan mode (BETA) loads the model package and uses it to look for the document and its position in the camera frame (more on that below). If no neural model package is found the success block will return the VerifaiAiError.neuralmodelNotFound error. In the example app we handle this by asking the user if he would like to use the manual mode instead. The manual mode allows the user to choose the document that will be scanned. It's then up to the user to scan the document correctly so that Verifai can process it. If the neural model package hasn't been loaded yet then the error block will return a VerifaiAiError.documentClassifierNeuralModelNotLoaded error.

    A note on performance

    While the ai mode is easier to implement and fast on newer devices it can feel sluggish on older generation devices. The manual scan mode is lighter and less resource intensive. Which is more suitable for older devices.

    Permissions

    Verifai uses the back camera of the iOS device to scan documents. To make sure this works you have to ask the user for permission to use the camera. To ensure this please make sure the Privacy - Camera Usage Description is added to your plist with a description. Verifai will handle the rest of the permission process and prompt the user for permission with the permission text you provide. If you don't ask permission from the user to use the camera but

    Automatic Scanning (BETA, iOS 11 and above)

    An easy way to start the ai scan view is by hooking a function up to a button that calls Verifai's scanViewController method

    /// Handle the tapping of the automatic scan button
    @IBAction func handleAutomaticScanButton() {
        Verifai.scanViewController(scanMode: .ai) { (autoScanVC) in
            // Present the scan VC
            autoScanVC.title = "Scan"
            self.navigationController?.pushViewController(autoScanVC,
                                                          animated: true)
            }
            .error { (error) in
                // An error occurred
                print("Error: \(error.description)")
            }
            .success { (data) in
                // Handle the response from Verifai
                if let response = data {
                    // Verifai detected a document!
                    print("Front image details: \(response.frontImage)")
                }
            }
    }
    

    Verifai contains a ViewController that facilitates the scanning of documents. To get this view controller simply call the Verifai.scanViewController(scanMode: .ai) method. If no error is found Verifai will return a viewController that allows a user to scan a document in a completion block. If you look at the example you can see how we return the autoScanVC in the block. Using this method sets the scan duration to its default value of 4 seconds.

    If you want fine grain control on the duration of the scanning you can use the Verifai.scanViewController(scanMode: .ai, minimalDetectionTime: Double) method. The minimalDetectionTime value represents the amount of seconds Verifai will look for the document before going to the confirmation screen.

    Once a user runs through the scan process the results will be returned in a VerifaiResponse object inside the call's success block. Make sure to implement the success and error blocks to handle any occurrences. This call does not return a progress block.

    Note: The .ai scan method is faster and easier to use, but it also needs more resources and might not be the best option for lower end devices.

    iOS 10: If you start the ai scan method on iOS 10 then the framework will return a VerifaiAiError.neuralModelNotSupported error inside the error block. Make sure to implement error block to ensure you handle errors.

    Manual scanning

    An easy way to start the manual scan view is by hooking a function up to a button that calls Verifai's scanViewController method

    /// Handle the tapping of the choose document button
    @IBAction func handleChooseDocumentButton() {
    
        Verifai.scanViewController(scanMode: .manual) { (manualScanVc) in
            manualScanVc.title = "Scan"
            self.navigationController?.pushViewController(manualScanVc,
                                                          animated: true)
            }
            .error { (error) in
                // An error occurred
             print("Error: \(error.description)")
            }
            .success { (data) in
               // Handle the response from Verifai
            if let response = data {
                // Verifai detected a document!
                print("Front image details: \(response.frontImage)")
            }
            }
    }
    

    Verifai contains a ViewController that facilitates the scanning of documents. To get this ViewController simply call the Verifai.scanViewController(scanMode: .manual) method. If no errors occur this method will setup and return (through a block, as seen in the example code) a UIViewController that leads the user through the process of selecting a document and scanning it.

    Once a user runs through the scan process the results will be returned in a VerifaiResponse object inside the call's completion block. Make sure to implement the completion block to get the result from Verifai.

    Note: The .manual scan method requires an internet connection to function correctly. If it's not available the error block will return either the VerifaiDownloadError.documentsDownloadFailed or VerifaiDownloadError.countriesDownloadFailed errors.

    Prefetching

    If your users are going to be using Verifai in situations where the internet connection isn't always guaranteed then it might be useful to prefetch the document data that the .ai scanning flow uses.

    An example of prefetching documents swift // Pre-fetch a certain country for faster processing Verifai.downloadDocuments(for: ["NL"]) .error({ (error) in // An error occurred print("Error: \(error.description)") }) .progress({ (progress) in print("⏱ Document download progress: \(progress)") }) .success({ (_) in print("🔥 Completed prefetching documents") })

    Verifai.downloadDocuments(for countries: [String])

    Call the downloadDocuments method with an array of country codes that should be fetched. Examples of country codes are: "NL", "DE" and "BE". You can follow the progress of the call by implementing the progress block. And once successfully completed the success block will be called . Any errors will be sent through the error block.

    If you don't prefetch the documents then Verifai will try to retrieve the document info on the fly while the user is scanning a document. The Verifai.cancelAllRequests() method can be called and all requests currently running will be cancelled if possible.

    Verifai.downloadCountries()

    An example of downloading the available countries swift // Start download of countries Verifai.downloadCountries() .error({ (error) in // An error occurred print("Error: \(error.description)") }) .success({ (countries) in print("🔥 Available countries: \(countries.description)") })

    To get a complete list of currently supported countries you can call the downloadContries() method. Once the countries are fetched then Verifai will return the countries as a [String] in the data element of the block.

    Progress & cancelling

    Certain download requests can take time to finish depending on the user's internet connection. To keep you as a developer in the loop we've added progress updates for certain calls that you can use to keep track of for example the downloadDocumentClassifierNeuralModels process. More on the blocks you can use to handle responses from Verifai below.

    Along the same vein we also give developers control over cancelling processes when they're taking too long (for example because of a bad internet connection). By calling the Verifai.cancelAllRequests() a message will be sent to all running calls to cancel as soon as possible (keep in mind a call may be too far along and will complete before being able to stop).

    Example of a progress block

    .progress({ (progress) in
        print("⏱ Document download progress: \(progress)")
    })
    

    Example of a error block swift .error { (error) in // Show error description print("Error: \(error.description)") // You can finely react to a certain error if error.type == .fatal { print("🚫 A fatal error occurred") } }

    Example of a success block swift .success { (data) in // Handle the response from Verifai if let response = data as? VerifaiResponse { // Response received print("Front image properties: \(response.frontImage)") } }

    Security features check

    The security features check is a way to validate the authenticity of a document. Many documents have very specific features that should be included on the document to prevent document forgery. These features can be checked against the document, such that the authenticity can be guaranteed.

    Example of security feature check

    let response: VerifaiResponse = someResponse // The response that was obtained earlier by a scan
    
    // Start the security feature flow
    Verifai.securityFeatureViewController(for: response) { (securityFeaturesVC) in
        // Push the given ViewController on the navigation stack
        self.navigationController?.pushViewController(securityFeaturesVC, animated: true)
    }
    // Handle any error
    .error { (error) in
        print("🚫 Error occurred while starting security features: \(error)")
    }
    // Handle success
    .success { (securityFeatureResponse) in
        // Return to the viewController that pushed the security feature flow
        self.navigationController?.popToViewController(self, animated: true)
    
        guard let securityFeatureResponse = securityFeatureResponse else { return }
    
        print("Passed security feature check? \(securityFeatureResponse.passed)")
    }
    

    Prerequisites

    In order to start the security features check a successful scan must be performed. This scan returns a VerifaiResponse, which needs to be passed to the security features check.

    Security feature scores

    Scores are used for security features to denote how much the feature counts for when it exists, the score of the security feature. We guarantee that for every document that has security features the sum of these scores will be >=1.0. You can set a custom scoreThreshold for the flow, such that the flow will stop when this threshold has been reached.

    Starting the security features check

    The security features check flow can be started by calling Verifai.securityFeatureViewController; which requires the following parameters:

    Property Description Type
    response The response that was obtained earlier by performing a scan VerifaiResponse
    scoreThreshold the threshold for the score. As soon as the score exceeds this threshold the flow is finished and no more security features will be checked. (default 1.0) Float
    present completion block that contains the View Controller to use for the security feature check (_ viewController: UIViewController) -> Void)

    For more info on the responses look here: Responses

    Notes

    Checking for security features without starting flow

    It is also possible to check for the availability of security checks before starting the flow. This can be useful when communicating this to the end-user.

    Example

    let response: VerifaiResponse = someResponse // The response that was obtained earlier by a scan
    
    // Start the security feature flow
    Verifai.securityFeaturesAvailable(for: response)
    // Handle any error
    .error { (error) in
        print("🚫 Error occurred while checking security feature availability: \(error)")
    }
    // Handle success
    .success { (hasSecurityFeatures: Bool?) in
        if let hasSecurityFeatures = hasSecurityFeatures, hasSecurityFeatures {
            // We have security features
        } else {
            // No security features
        }
    }
    

    Visual Inspection Zone (VIZ) check

    Example of using the VIZ check

    // Make sure we have the necessary data
    guard var documentData = verifaiResponse else {
        return
    }
    // We have a preference for the NFC MRZ data but otherwise use the scan MRZ data
    if let verifiedMrzData = nfcData?.validatedDocumentData {
        documentData.mrzModel = verifiedMrzData
    }
    // Start the VIZ check
    Verifai.startVizCheck(documentData: documentData,
                          nfcImage: self.nfcData?.documentImage,
                          present: { (vizViewController) in
                            vizViewController.title = "VIZ check"
                            self.navigationController?.pushViewController(vizViewController,
                                                                          animated: true)
    
    })
    // Handle the success
    .success { (data) in
        if let response = data {
            // Return to this screen
            self.navigationController?.popToViewController(self, animated: true)
            // Parse results
            print("Passed all tests? \(data.passedAll)")
        }
    }
    // Handle any errors
    .error { (error) in
        DispatchQueue.main.async {
            self.navigationController?.popToViewController(self, animated: true)
            // An error occurred
            print("Error: \(error.description)")
        }
    }
    

    One of the steps of confirming the information on a eMRTD document is inspecting its Visual information zone. The inspection zone is on the front of the document. Verifai facilitates a simple check to determine if the information on the front of the document matches the information scanned from the Machine Readable Zone.

    Prerequisites

    Before starting the VIZ check a successful scan must be performed. This will give you the data needed to perform the VIZ check. Optionally you can also perform an NFC check and send this data along to the VIZ check (More on the NFC scan below).

    Starting the VIZ check

    Call Verifai.startVizCheck() to start the VIZ check. Implement the .success and .error blocks to handle the results and any error that can occur. The call needs the following parameters.

    Property Description Type
    documentData The document data received from a successful scan VerifaiResponse
    nfcImage The NFC image from a successful NFC scan (optional) UIImage?
    present completion block that contains the View Controller to use for the VIZ check (_ viewController: UIViewController) -> Void)

    VIZ check results

    The VIZ check will return a VerifaiVizCheckResponse object. To simply test if all checks were passed you can check the passedAll boolean. Furthermore the response will contain a dictionary with the title of the zone that was checked and whether the test was passed or not. An example of this can be seen in the VerifaiVizCheckResponse part of the Responses chapter below. It's up to you the developer to give significance to the result. A failed test might mean that the MRZ was read incorrectly, that the user made a mistake or that the value is actually different on the document.

    Lifeness checks

    Default lifeness checks implementation

    private func startLifenessCheck() {
            Verifai.startLifenessCheck(locale: Locale.current) { (lifenessCheckVC) in
                // Present the view
                self.navigationController?.pushViewController(lifenessCheckVC, animated: true)
            }
            .success { (lifenessCheckResponse) in
                // Handle the response
            }
            .error { (error) in
                // Handle the error
            }
    }
    

    The lifeness checks allow you to check if the person whose document you're scanning is really the one using the app at the moment. By using the VerifaiLifenessCheck object you can set a few simple tasks that the user can perform to confirm he or she is alive. We also built in 4 default checks that allow you to simply start the lifeness check and will be performed.

    Permissions

    For the lifeness checks to work correctly you need to implement the following permissions in your app:

    Permission What Verifai uses it for
    NSMicrophoneUsageDescription Needed to record the lifeness check audio and perform speech recognition
    NSSpeechRecognitionUsageDescription Needed to perform the speech recognition check

    Starting the lifeness check

    The lifeness features check flow can be started by calling Verifai.startLifenessCheck; which requires the following parameters:

    Property Description Type
    locale (optional) The locale (usually current) that will be used in speech recognition tasks Locale?
    requirements (optional) You can optionally provide your own checks that the user can perform. As long as they use our structure [VerifaiLifenessCheck]?
    present completion block that contains the View Controller to use for the security feature check (_ viewController: UIViewController) -> Void)

    The locale is only needed when a speech recognition task is needed. If it's not set an invalidLifenessCheck error will be sent via the error block. Before lifeness checks start we will check if there are at least 50 mb available of space on the device. If there isn't the framework will return a VerifaiCriticalError.lowSpaceForOperationWarning via the error block. Less than 50 mb can mean that videos are not saved correctly once a user is done with the check.

    Structure of the lifeness check

    A VerifaiLifenessCheck can be one of the following:

    Object Check Description Initializer
    VerifaiEyesLifenessCheck Close eyes check A user is asked to close their eyes for a certain amount of time init(instruction: String, closedEyesRequirement: Bool, duration: Float)
    VerifaiTiltLifenessCheck Head tilt check A user is asked to tilt their head up to a certain point (customisable) init(instruction: String, faceAngleRequirement: Float)
    VerifaiSpeechLifenessCheck Speech recognition test A user is asked to say certain words (Siri required) init(instruction: String, speechRequirement: String)

    Verifai provides you the option to just start the lifeness check and we'll use the following default tasks:

    Task Description
    Tilt your head left The user is asked to tilt their head 15 degrees to the left
    Tilt your head right The user is asked to tilt their head 15 degrees to the right
    Close eyes for 2 seconds The user is asked to close their eyes for 2 seconds
    Speak the following words The user is asked to say 3 names of fruits

    Alternatively you can get an array of the default requirements and edit them to suit your needs by calling: Verifai.defaultLifenessCheckRequirements().

    Customizing lifeness checks

    You can customize the structure of a lifeness when you including it:

    VerifaiEyesLifenessCheck

    Variable Description Type Example
    instruction The instruction that is shown to the user String Close your eyes for 2 seconds
    closedEyesRequirement Wether this check requires the user to close their eyes Bool false
    duration The duration of the closed eyes check Float 2

    VerifaiTiltLifenessCheck

    Variable Description Type Example
    instruction The instruction that is shown to the user String Tilt your head to the left
    faceAngleRequirement The angle a user has to tilt their head Float -25

    VerifaiSpeechLifenessCheck

    Variable Description Type Example
    instruction The instruction that is shown to the user String Say the following words:
    speechRequirement The words a user has to say to pass the check String Apple Banana Orange

    Once the check is done Verifai will return the requirements that where used by the system only this time including 2 extra variables with the results:

    Variable Description Type Example
    recordingUrl The local url of the video file of the user performing the test URL? file:///var/mobile/Containers/Data/Application/E6B60B40-8834-45FC-A5B0-7B4C1D94204E/Documents/Verifai/LifenessChecks/lifenessCheck16-Nov-2018-171926Z.mp4
    status The status of the check VerifaiLifenessCheckResult skipped

    The result can be one of the following types:

    Type Description
    passed The check was performed successfully
    skipped The check was skipped by the user
    notStarted The check has not been performed

    Deleting the lifeness checks

    After completing the lifeness checks we provide you with a local url with the location of the user performing the check. Please keep the privacy of your users in mind and delete the file once it is no longer needed. We store the videos in the app's Documents folder. Inside the LifenessChecks folder inside the Verifai folder. Each filename is time stamped to help determine recording time if needed.

    To make the process of deleting the videos as easy as possible we also provide the Verifai.removeLifenessChecks() method. This method will delete all the contents of the LifenessChecks folder.

    Responses

    VerifaiResponseBlock<T>

    Verifai returns success, errors, progress and messages through a responseblock. You can choose to which blocks to listen to and in this way decide what info you process.

    Currently the blocks are one of the following:

    Property Description Type
    progress(_ progress: Int) The process returned a progress update Int 0-100
    success(data: T) The call was completed T changes depending on the call.*
    error(_ error: VerifaiMessage) An error occurred check the VerifaiMessage for more info. Or de error section below i.e. VerifaiFatalError or VerifaiAiError etc
    info(_ info: VerifaiMessage) Information that can be useful but is not an error. i.e. VerifaiNFCInfo

    (*) i.e. a list of countries in [String] or VerifaiResponse when the scanning succeeds, or nil if no data is returned.

    VerifaiSecurityFeatureResponse

    Property Description Type
    score The score that was obtained when going through the security feature flow Float
    maximumScore The maximum score that one can obtain for the document that was getting checked Float
    threshold Threshold that was used to determine when the flow would stop Float
    passed Shorthand for score >= threshold Float

    VerifaiResponse

    When document scanning is completed successfully then a VerifaiResponse object will be returned. This object contains the following data:

    Property Description Type
    frontImage Image of the front of the document if available UIImage
    backImage Image of the back of the document if available UIImage
    mrzData The MRZ data of the document if available VerifaiMrzModel
    documentType The document type based on what the neural network found VerifaiDocumentType
    issuingCountryString The issuing country based on what the neural network found String i.e. "NL"
    documentHasMrz Whether the scanned document has an MRZ Bool

    Note: MRZ stands for Machine Readable Zone. Some documents have an MRZ with information about the person on the document. Verifai tries to read this information will return it if available. See below for a description of VerifaiMrzModel

    MRZ

    VerifaiMrzModel

    When a machine readable zone is parsed the framework will return this model. We do a best effort to find the values so please ensure they exist when trying to use them. Please keep privacy law in mind when requesting and using certain data.

    Property Description Type example td1 td2 td3 mrva mrvb Dutch Driving Licence French ID
    mrzString The complete MRZ separated by \n String I<NLDSPECI20142999999990<<<<<8\n
    6503101F2403096NLD<<<<<<<<<<<8\n
    DE<BRUIJN<<WILLEKE<LISELOTTE<<\n
    X X X X X X X
    documentType The type of document VerifaiDocumentType idCard X X X X X X X
    documentSubType The second letter of the MRZ indicating a specific sub type String I X X X X X X X
    countryCode The alpha3 country code on the MRZ String NLD X X X X X X X
    lastName The last name (separated by a space if multiple) String DE BRUIJN X X X X X - X
    firstName The first name (separated by a space if multiple) String WILLEKE LISELOTTE X X X X X - X
    documentNumber The document number String SPECI2014 X X X X X X X
    documentNumberCheckDigit The check digit of the document number String 2 X X X X X - X
    nationality The nationality of the document holder String NLD X X X X X - -
    dateOfBirth The date of birth of the document holder Date Mar 10 1965 X X X X X - X
    dateOfBirthCheckDigit The check digit of the date of birth String 1 X X X X X - X
    sex The sex of the document holder as described on the document String F X X X X X - X
    dateOfExpiry The date of expiry of the document Date Mar 09 2024 X X X X X - -
    dateOfExpiryCheckDigit The check digit on the date of expiry String 6 X X X X X - -
    personalNumber The personal number on the document String 999999990 - - X - - - X
    personalNumberCheckDigit The check digit of the document number String 8 - - X - - - -
    biometricPassportNfcKey The ICAO standaard key used for communicating with the NFC on the document String SPECI2014265031012403096 X X X X X - -
    isBiometricPassportNfcKeyValid Whether the calculated key is valid Bool true X X X X X - -
    driversLicenceNfcKey The NFC key used for communicating with the NFC on the document String 1NLD150949621118QJ3GL63QT6T5 - - - - - X -
    isDriversLicenceNfcKeyValid Whether the calculated key is valid Bool true - - - - - X -
    driversLicenceVersion The version of the dutch driver's licence String 1 - - - - - X -
    driversLicenceRandomItem Random data string on dutch driver's licences String 8QJ3GL63QT6T5 - - - - - X -
    driversLicenceChip The version of the chip on the document String 1 - - - - - X -
    departmentOfIssuance The department of issuance of this document String 728 - - - - - X X
    officeOfIssuance The office of issuance of this document String 134 - - - - - X X
    optionalDataStringOne Optional data on the MRZ String 999999990<<<<<8 X X - X X - -
    optionalDataStringTwo Second optional data line on the MRZ String 075405222 X - - - - - -
    compositeCheckDigit A check digit of one or several lines on the MRZ String true X X X - - - X
    validityScore A validity score calculated by how many validity checks where passed Int 0 - 100 100 X X X X X X X
    isDocumentNumberValid A boolean indicating whether the document number is valid Bool true X X X X X - X
    isDateOfBirthValid A boolean indicating whether the date of birth is valid Bool true X X X X X - X
    isDateOfExpiryValid A boolean indicating whether the date of expiry is valid Bool true X X X X X - -
    isCompositeValid A boolean indicating whether the document MRZ is valid Bool true X X X - - - X
    isLine1LengthValid A boolean indicating whether a line length is valid Bool true X X X X X X X
    isLine2LengthValid A boolean indicating whether a line length is valid Bool true X X X X X - X
    isLine3LengthValid A boolean indicating whether a line length is valid Bool true X - - - - - -
    isDocumentCodeValid A boolean indicating whether the document code is valid Bool true X X X X X X X
    isPersonalNumberValid A boolean indicating whether the personal number is valid Bool true - - X - - - -
    mrzFormat The format of the MRZ, more on the different types of format below VerifaiMrzFormat td3 X X X X X X X

    VerifaiMrzFormat

    There are several formats of MRZ. Our parser will try to guess the format and will populate MrzData field according to the format.

    Format Line count Character count per line
    td1 3 30
    td2 2 36
    td3 2 44
    mrva 2 44
    mrvb 2 36
    Dutch Driving Licence 1 30
    French ID 2 36

    VerifaiDocumentType

    A document can be one of the following types:

    Type Description
    idCard Identification card
    driversLicence Driver's licence
    passport Passport
    refugee Refugee travel document
    emergencyPassport Emergency passport
    residencePermitTypeI Residence permit type I/III/EU
    residencePermitTypeII Residence permit type II/IV
    visa Visa
    unknown Unknown document

    VerifaiVizCheckResponse

    Name Description Type Example
    passedAll Describes if all test passed or not Bool true
    performedChecks A dictionary that contains the performed check (as key) and the result (value) [String : Bool] DATE_OF_BIRTH,SEX,DATE_OF_EXPIRY,ISSUING_COUNTRY,SURNAME,GIVEN_NAME

    VerifaiLifenessCheckResponse

    The response of the Lifenesscheck contains a VerifaiLifenessCheck array (contains types VerifaiEyesLifenessCheck,VerifaiTiltLifenessCheck or VerifaiSpeechLifenessCheck) that contains the requirements that were used during the test. Each requirement also contains the result of the test and a local URL where the recording of the test can be found.

    Name Description Type
    requirements The requirements that where used during the test supplemented with their results [VerifaiLifenessCheck]

    Errors

    VerifaiMessage

    VerifaiMessage is the protocol that all errors and info objects coming from Verifai conform to. A VerifaiMessage has a type, a description and an error structure for easy error tracking (more on that below). Errors returned by the error block can be one of the following:

    Fatal errors:
    Errors the SDK can't recover from

    Error Description VerifaiErrorType
    VerifaiFatalError.licenceNotValid When a licence is invalid then enum is returned as an error. A licence could be expired or erroneously configured. You may for example alert the user or log this to your own systems. .fatal

    System warnings:
    System warnings or errors sent trough the info block

    Error Description VerifaiErrorType
    VerifaiSystemWarning.memoryWarning Verifai received a memory warning from the system. .system

    Critical errors:
    Errors that should be handled immediately or are time sensitive

    Error Description VerifaiErrorType
    VerifaiCriticalError.lowSpaceForOperationWarning Verifai might not have enough space on the device to handle the current operation correctly. .critical
    VerifaiCriticalError.speechRecognitionError Speech recognition unavailable, an unspecified error occurred. .critical
    VerifaiCriticalError.speechRecognitionTimeout Speech recognition time limit reached. .critical

    System errors:
    Errors or warnings that the framework received from the system

    Error Description VerifaiErrorType
    VerifaiSystemError.memoryWarning Verifai received a memory warning from the system. .critical
    VerifaiSystemError.lifenessCheckRemovalNotPossible We were unable to remove the lifeness checks videos, or no videos were present. .critical

    Ai errors:
    Errors relates to the neural models

    Error Description VerifaiErrorType
    VerifaiAiError.neuralmodelNotFound The .ai scanning mode and OCR parsing mode require a model to be set. These models are available from the Verifai backend and are set with the Verifai.setDocumentClassifierNeuralModel() or setOCRMRZNeuralModel() methods respectively. .ai
    VerifaiAiError.neuralModelNotEnoughSpace There wasn't enough space to save the neural model .ai
    VerifaiAiError.neuralModelNotSupported iOS 10 doesn't support our neural models. Please use iOS 11 and above .ai
    VerifaiAiError.documentClassifierNeuralModelNotLoaded Returned when you call a function that requires the document classifier neural model to be loaded but this hasn't been performed (yet) .ai
    VerifaiAiError.ocrNeuralModelNotLoaded Returned when you call a function that requires the ocr classifier neural model to be loaded but this hasn't been performed (yet) .ai
    VerifaiAiError.neuralModelInvalid The neural model package is invalid .ai

    Configuration errors:
    Errors related to the user's settings

    Error Description VerifaiErrorType
    VerifaiConfigurationError.cameraPermissionUnauthorized Verifai is not authorized to use the camera because of the user's privacy settings .configuration
    VerifaiConfigurationError.cameraPermissionNotGranted Permission to use the camera has just been denied after showing the user the permission prompt .configuration
    VerifaiConfigurationError.vizCheckDataInvalid The VIZ check could not be performed because the structure of the data appears to be invalid .configuration
    VerifaiConfigurationError. vizCheckMrzInvalid The VIZ check could not be performed because the scanned MRZ appears to be incomplete or empty .configuration
    VerifaiConfigurationError.vizCheckInsufficientData The VIZ check could not be performed because there aren't enough unblocked zones available .configuration
    VerifaiConfigurationError.responseIncomplete The security features flow could not be started because the VerifaiResponse which should be used for the flow doesn't contain the required values. .configuration
    VerifaiConfigurationError.invalidLifenessCheck The Lifeness Check requirement is invalid. Consult the documentation for more info on setting up lifeness checks .configuration
    VerifaiConfigurationError.speechRecognitionNotAvailable Speech recognition not available for this locale, is disabled or internet connection unavailable .configuration
    VerifaiConfigurationError.microphonePermissionNotGranted Permission to use the microphone has just been denied .configuration
    VerifaiConfigurationError.speechRecognitionPermissionNotGranted Permission to use speech recognition has just been denied .configuration
    VerifaiConfigurationError.internalAVError Unable to create an Audio/Video component to record the lifeness checks. Try again. .configuration

    Download errors:
    Errors related to download processes

    Error Description VerifaiErrorType
    VerifaiDownloadError.invalidDownloadRequest The parameters you supplied while making the request are invalid (i.e. wrong country codes). Check your request and try again. .download
    VerifaiDownloadError.neuralModelDownloadFailed Downloading a neural model failed. We suggest asking a user to check his internet connection if this error occurs. .download
    VerifaiDownloadError.documentsDownloadFailed Before running the scan views you may choose to preemptively download the document information that Verifai needs to process documents (with the Verifai.downloadDocuments method). Also when using the .manual scan method Verifai will download the documents the user selects from the Verifai servers. We suggest asking a user to check his internet connection if this error occurs. .download
    VerifaiDownloadError.documentsNotFound When using the Verifai.downloadDocuments(for countries: [String]) method you can supply one or more country codes (i.e. NL). It might happen that a counties documents are not available on Verifai. Verifai will return the documentsNotFound to inform the app that the documents are not (currently) available. .download
    VerifaiDownloadError.countriesDownloadFailed When using the .manual scan method Verifai will ask the user of the app to choose a document group from a list of available countries. Or you can call the downloadCountries() method to get a list of available countries. If this call fails then the countriesDownloadFailed error enum will be called. This is usually an indication of there not being an active internet connection. .download
    VerifaiDownloadError.securityFeaturesNotFound When using the security features check flow, this error is returned when no security features exist for the given document. .download
    VerifaiDownloadError.securityFeaturesDownloadFailed When using the security features check flow, this error is returned when an error occurs when downloading the security features from the backend .download

    Additionally if the licenceNotFound error is returned more info can be obtained by calling its licenceInfo() method.

    .error { (error) in
        // Track only fatal and critical errors
        if error.type == .critical || error.type == .fatal {
            print("A critical or fatal error occurred: \(error.description)")
        }
        // Track only individual errors
        if error.ai.notSupported || error.configuration.cameraUnauthorized {
            print("A very specific error occurred")
        }
    }
    

    Error structures

    Verifai also provides several structures to facilitate easy error detection. We recognize that certain flows don't need to track all possible errors. Check out the example code to see some examples of simple error tracking by listening to the type of an error. Or one (or several) specific errors on the block.

    Verifai NFC

    Verifai offers the ability to scan the NFC chip on documents that follow the ICAO NFC standaard. We're able to do this by using an external NFC reader that connects to Verifai through Bluetooth.

    Prerequisites

    For some basic info on eMRTD: NFC eMRTD Check

    Getting started

    Before being able to perform an NFC check You can use the isBiometricPassportNfcKeyValid and isDutchDrivingLicenceNfcKeyValid variables from the VerifaiMrzModel object to check if the data is valid for scanning the document through NFC.

    Example on how to use the NFC flow

    if let documentData = verifaiResponse {
            // Start the NFC process
            VerifaiNFC.readNfc(documentData: documentData,
            .error({ (error) in
                if let message = error as? VerifaiNFCError {
                    if message.nfc.connectionFailedBluetooth || message.nfc.connectionFailed {
                        state = .notConnected
                    } else {
                        state = .failed
                    }
                }
    
            })
            .info({ (info) in
                if let message = info as? VerifaiNFCInfo,
                    message.nfc.connected {
                    state = .preparingScan
                }
            })
            .progress({ (progress) in
                state = .scanning
                progress = Float(progress) / Float(100)
            })
            .success({ (data) in
                state = .success
                // Process response
                print("Did the scan complete?: \(data.scanCompleted)")
            })
    }
    

    VerifaiNFC.readNfc(documentData: VerifaiResponse, retrieveImage: Bool, readerSerial: String)

    Using the VerifaiNFC.readNfc(documentData: VerifaiResponse, retrieveImage: Bool, readerSerial: String) method the NFC scanning can be started. Pass the VerifaiResponse that you receive from a successful Verifai scan in the documentData variable. Verifai will take care of the rest by trying to connect to the NFC reading device and scanning the card (once placed on the reader).
    The retrieveImage allows you to select whether the biometric image on the document should be retrieved or not. Retrieving the biometric image adds considerable time to the scanning process. You should only do this if necessary.
    The readerSerial allows you to specify the serial number of a supported NFC reader so that Verifai van only connect to one NFC reader.

    VerifaiNFC.closeNFCConnection()

    Once the read function is triggered the reader will wait for a document to be presented. After a successful scan the reader will still await another document. This allows multiple cards to be read one after the other. If however you want to close the connection (for example when abandoning and NFC scan flow) then it's your responsibility to close the connection to the reader. You can do this by calling the VerifaiNFC.closeNFCConnection() function.

    VerifaiNFCResponse

    Using the VerifaiNFCResponse you can access the NFC scan result. The whole process of scanning and verifying the different elements of the document is available in this object. Keep in mind even if an error occurs verify will send a VerifaiNFCResponse that will give clues of the state of the card that we were able to read.

    Field Type Description
    scanCompleted Bool Whether the whole scan process was completed or not. False if an error occurred while scanning
    documentImage UIImage? The picture from the document, if requested
    sodHashes [UInt8: Data]? Dictionary containing the EF.SOd checksums
    computedHashes [UInt8: Data]? Dictionary containing the checksums we computed during scanning
    dataGroups [UInt8: Data]? Dictionary containing the raw read data
    validatedMrzData VerifaiMrzModel? MrzData object parsed from data on the chip if available
    nlEdlNFCData VerifaiNFCNlEdlData? Exclusive to the Netherlands, any document data available on the driver's licence NFC chip.
    mrzMatch Bool Does the MRZ from the OCR match the MRZ from the card*
    comSodMatch Bool Does the EF.COM data match the datagroups in EF.SOd
    bacStatus VerifaiBacStatus The status of granting access to the card
    activeAuthenticationStatus VerifaiActiveAuthStatus The status of the active authentication check
    chipAuthenticationStatus VerifaiChipAuthStatus The status of the chip authentication check
    passiveAuthenticationStatus VerifaiPassiveAuthStatus The status of the passive authentication check
    validDocumentSigner Bool Is the document signed by the certificate from the EF.SOd
    validCertificate Bool Is the certificate from the EF.SOd valid
    rootCertificateStatus VerifaiRootCertificateStatus The status of the root certificate check
    documentCertificateSummary String A short summary of the document certificate
    rootCertificateSummary String A short summary of the root certificate
    authenticity Bool Did both passiveAuthenticationStatus and validDocumentSigner pass their checks
    confidentiality Bool Did both chipAuthenticationStatus and bacStatus pass their checks
    originality Bool Did both chipAuthenticationStatus and activeAuthenticationStatus pass their checks

    VerifaiBacStatus enum

    The VerifaiBacStatus status can be one of the following:

    Status Description
    .success Chip was accessed successfully
    .failed Failed to gain access to chip
    .notChecked Process has not been performed yet
    .randomFailed The chip did not return a challenge

    VerifaiActiveAuthStatus enum

    The VerifaiActiveAuthStatus status can be one of the following:

    Status Description
    .success Active authentication was performed successfully
    .signatureFailed Unable to verify the public key on the chip
    .failed Active authentication failed
    .notPresent Active authentication not present on the chip
    .notChecked Active authentication process hasn't been performed

    VerifaiChipAuthStatus enum

    The VerifaiChipAuthStatus status can be one of the following:

    Status Description
    .success Chip authentication was performed successfully
    .failed Chip authentication failed
    .notPresent Chip authentication not present on the chip
    .notChecked Chip authentication process hasn't been performed

    VerifaiPassiveAuthStatus enum

    The VerifaiPassiveAuthStatus status can be one of the following:

    Status Description
    .success Passive authentication was performed successfully
    .failed Passive authentication failed
    .notPresent Passive authentication not present on the chip
    .notChecked Passive authentication process hasn't been performed

    VerifaiRootCertificateStatus enum

    The VerifaiRootCertificateStatus status can be one of the following:

    Status Description
    .notChecked Passive authentication process hasn't been performed
    .valid The root certificate is valid
    .invalid The root certificate is invalid
    .revoked The root certificate has been revoked
    .notFound We were unable to find the root certificate

    VerifaiNFCNlEdlData

    Exclusive to The Netherlands, Verifai can also read data from the Dutch driver's licence. If available we put any available info of the document holder into the VerifaiNFCNlEdlData object. Some variables may or may not be available depending on the chip.

    Variable Description Type Example
    issuingMemberState The state issuing the driver's licence String? NLD
    lastName The last name(s) of the document holder String? Vanderlinde e/v Zeilemaker
    firstName The first name(s) of the document holder String? Jade F K
    dateOfBirth The date of birth of the document holder Date? 1981-02-17 00:00:00 +0000
    placeOfBirth The place of birth of the document holder String? Groningen
    dateOfIssue Date when the document was issued Date? 2018-05-12 00:00:00 +0000
    dateOfExpiry Date when the document expires Date? 2024-11-15 00:00:00 +0000
    issuingAuthority The authority that issued the document String? Gemeente Veendam
    documentNumber The document's document number String? 5094962111
    bsn The document holder personal number String? 000000000
    nationality The nationality of the document holder String? NLD
    sex The document holder's gender String? F
    surnameICAO The surname of the document holder in ICAO notation String? VANDERLINDE EV ZEILEMAKER
    firstNameICAO The first name of the document holder in ICAO notation String? JADE F K
    surnameGBA The surname of the document holder in GBA notation String? Vanderlinde e/v Zeilemaker
    firstNameGBA The first name of the document holder in GBA notation String? Jade F K
    placeOfBirthGBA The place of birth of the document holder in GBA notation String? Groningen
    countryOfBirthGBA The country of birth of the document holder in GBA notation String? Venezuela

    VerifaiNFCError

    NFC errors are passed through the error block (see example next to VerifaiNFC.readNfc(documentData: VerifaiResponse, retrieveImage: Bool, readerSerial: String))

    Error Description
    .nfcReaderConnectionFailed The connection to the NFC reader failed
    .nfcReaderConnectionFailedBluetooth The connection to the NFC reader failed because bluetooth is off or not authorized
    .nfcReadError Reading the NFC chip failed (you can retry)

    NFC eMRTD Check

    Verifai can read and validate biometric passports (also known as an e-passport, ePassport or digital passport).

    All eMRTDs are marked with the following symbol (Chip inside symbol).

    Chip inside symbol

    eMRTD Security measurements

    To check a NCF Chip Verifai executes a lot of checks as defined below:

    Protocol Protects
    Basic Access Control Confidentiality
    Passive Authentication Authenticity
    Active Authentication Originality
    Chip Authentication Originality + confidentiality

    Security measurements:

    Basic Access Control (BAC)

    The purpose of Basic Access Control is to ensure that the we have physical access to the eMRTD. BAC prevents skimming and also eavesdropping of the communication between passport and Verifai. BAC requires the knowledge of the eMRTD holder prior to being able to read the chip. This information is retrieved from the MRZ.

    Passive Authentication (PA)

    A eMRTD contains a Document Security Object(EF.SOd), which includes the digital signature of the the issuing State and in which hash representations of the LDS contents are stored. The goal of Passive Authentication is to prove the authenticity of the data contained in the LDS.

    Passive Authentication checks:

    Active Authentication (AA)

    Active Authentication verifies that the chip is genuine. By implementing the optional Active Authentication protocol eMRTDs becomes protected against chip substitution. This is ensured by the means of a challenge-response protocol between Verifai and the eMRTD chip using asymmetric cryptography.

    Chip Authentication (CA)

    Chip Authentication also verifies that the chip is genuine. By implementing the optional Chip Authentication protocol eMRTDs becomes protected against chip substitution.

    Customizing the views

    Verifai uses a template system to allow devs to customize the look of the colors in the scan view. You need to create a .plist file in your project. Afterwards you can call Verifai.template(path: URL?) with the path to the plist file.

    You can download an example template file with the default Verifai styling here: example plist.

    Add one or all of the following keys with the right value and they will affect the look and feel of Verifai. Be aware that some of these values can be overridden by the systems appearance proxy. It is possible to refer to other fields in the plist. For example, many elements refer to PrimaryColor, such that these colors can easily be changed, while still allowing more granular control when necessary.

    Value Description Default
    PrimaryColor The primary color used for tinting in the application watermelon #fc596f
    SecondaryColor The secondary color used for tinting -- used primarily for text or UI elements that are placed over the PrimaryColor white #ffffff
    BackgroundColor The background color of the app views white #ffffff
    TextColor The color of text labels through-out the app black #000000
    InvertedTextColor Color of text placed in the bottom box white #ffffff
    AffirmativeButtonBackgroundColor The background color of the buttons that inform the user he can continue or select something green #5FB501
    DestructiveButtonBackgroundColor The background color of the buttons that reset or delete states orange #f26522
    ButtonTextColor The text color for the buttons InvertedTextColor
    NavBarBackgroundColor The background color of the navigationBar, loading indicators and info labels PrimaryColor
    TextHighlightColor The color of text which is highlighted, for example when a country or document is chosen with a checkmark PrimaryColor
    DocumentScanHighlightColor Color for the document scan highlight in the scanview PrimaryColor
    ScanHighlightTextColor Color for the document scan highlight in the scanview SecondaryColor
    BottomBoxBackgroundColor Background color for the bottom box used throughout multiple views nero #212121
    BottomBoxImageTintColor Tint color for template drawn images used inside the bottom box InvertedTextColor
    BottomBoxTitleTextColor Text color for the title inside the bottom box PrimaryColor
    BottomBoxTextColor Text color for the main text inside the bottom box InvertedTextColor
    BottomBoxTextAreaTextColor Text color for the text inside a textarea inside the bottom box InvertedTextColor
    BottomBoxTextAreaBackgroundColor Background color for text areas inside the bottom box neroLight #333333
    BlockedFieldColor The color of blocked off fields in the scan view. black #000000
    BottomBoxSubtitleTextColor Text color for the small and dark text in the bottom box, commonly used as a subtitle (e.g. percentage in security view) lightGray #ababab
    BottomBoxSeparatorBackgroundColor Color used for the title/content separator in the bottom box neroLight #333333
    SelectDocumentTypeChevronColor Color used for the chevrons when there are many documents to choose from in the custom document picker InvertedTextColor
    SelectDocumentTypePaginationCircleColor Color for the pagination circles in the custom document picker white #ffffff

    Example app

    All the concepts documented so far have also been included in the example app that you can download to see a working example of Verifai.

    Example app on Github