Skip to content

Subject Manager

A Subject consists of a set of data that represents a digital ID in the enrolment process. The Mobile ID SDK provides the ability to build and create such Subject.

Use Subject Manager

You can manage subjects using the subject management functions available in the enrolment facade. Subjects are the result of the enrolment process and need to be successfully created in the server-side platform, to complete any process requiring a digital form of identification. The subject will always be null when returned from the facade's method subject builder. This field will only be filled by our backend services, depending on if the subject is fully authenticated or not. If the user is not fully authenticated, the subject token will be filled only on when the app fetches the subject on the facade's get subject method. If the subject token is not null, it will contain a base 64 QR code that the user must present on the physical gate for a full authentication.

data class Subject(
    val id: String?,
    val language: String,
    val document: Document,
    val biometrics: List<Biometric>,
    val validationStatus: ValidationStatus,
    val subjectToken: SubjectToken?,
    var boardingPass: BoardingPass? = null
)
public struct Subject {
    public let id: String
    public let document: Document
    public let biometrics: [Biometric]?
    public var boardingPass: BoardingPass?
    public var validationStatus: ValidationStatus
    public var subjectToken: SubjectToken?
    public var language: Locale
    public var formData: [FormAnswer]?
}

The subject id will be generated and returned upon adding it for the first time.

The SubjectToken will have the following structure:

The SubjectToken will also provide a method to return the QR code as a bitmap. For this you just need to call the method getQRCodeImage().

data class SubjectToken(
    val qrCodeBase64: String
)
public struct SubjectToken {
    public var qrCodeBase64: String
    public var qrCodeImage: UIImage?
}

The ValidationStatus will have the following structure:

data class ValidationStatus(
    val documentAuthenticated: Boolean,
    val livenessCheckPassed: Boolean,
    val biometryMatched: Boolean
)
public struct ValidationStatus {
    /// Field indicating if the Enrolment photos matched.
    public var biometryMatched: Bool
    /// Field indicating if the liveness check test was performed with success.
    public var livenessCheckPassed: Bool
    /// Field indicating if the RFID scan of the document was performed with success.
    public var documentAuthenticated: Bool
}

Check the Document, BoardingPass and Biometric pages for more information about the remaining attributes.

Subject operations

You can create a new subject using the addSubject method. Subject data can be automatically submitted to the server-side platform after a successful document read and face matching operation, as shown in the following example:

/**
 * Adds a [Subject].
 *
 * @param context Context
 * @param params contains instance from [Subject] that will be added in the server.
 * @param resultLauncher [ActivityResultLauncher<Intent>] fragment or activity that will handle the results .
 */
fun addSubject(
    context: Context,
    params: AddSubjectParameters,
    resultLauncher: ActivityResultLauncher<Intent>
)
/// Saves a Subect to server.
/// - Parameters:
///   - parameters: Parameteres for the Add Subject flow.
///   - viewController: View controller base from when to present required SDK view controllers.
///   - completionHandler: The completion handler to call when the add subject operation is finished.
///     This completion handler takes the following parameter:
///
///     Result<Void, SubjectError>
///     Where `SubjectError` contains the possible errors that may occur during the process.
func addSubject(parameters: AddSubjectParameters, viewController: UIViewController, completionHandler: @escaping (Result<Void, SubjectError>) -> Void)

If you just need to build a subject, you must have the documentData object. The documentData must be filled by the client app, or it can be acquired from the DocumentReaderReport when scanning with the SDK scan document feature. The documentPhoto and enrolmentPhoto can be acquired from the Document Reader and the Face Capture features respectively. The boardingPass object can be acquired from the Boarding Pass Reader. The matchReport is a result of using the facade's face match feature. The language field is, by default, the locale in use on the device, allows the user to continue the enrollment in a kiosk with the correct language and it can be changed by client apps. With this data you can create the BuildSubjectParameters object. This object has the following structure:

data class BuildSubjectParameters(
    val documentData: DocumentData,
    val documentPhoto: Bitmap?,
    val enrolmentPhoto: Bitmap,
    val boardingPass: BoardingPass? = null,
    val processReport: FaceCaptureReport? = null,
    val matchReport: MatchReport? = null,
    val documentReaderReport: DocumentReaderReport? = null,
    val language: Locale? = null,
)
public struct BuildSubjectParameters {
    public let documentData: DocumentData?
    public let idDocument: IdDocument?
    public let documentDataValidated: Bool
    public let documentImage: UIImage
    public let enrolmentImage: UIImage
    public let boardingPass: BoardingPassSummary?
    public let language: Locale
    public let formReport: FormReport?
    public let processReport: BiometricFaceCaptureReport?
    public let documentReaderReport: DocumentReaderReport?

    public init(
        documentData: DocumentData? = nil,
        idDocument: IdDocument? = nil,
        documentImage: UIImage,
        enrolmentImage: UIImage,
        boardingPass: BoardingPassSummary?,
        documentReaderReport: DocumentReaderReport? = nil,
        biometricFaceCaptureReport: BiometricFaceCaptureReport? = nil,
        matchReport: MatchReport? = nil,
        language: Locale? = nil,
        formReport: FormReport? = nil
    )
}

The following example shows how you can build a subject:

launch {
    val params = BuildSubjectParameters(
        documentData = documentData,
        documentPhoto = documentPhoto,
        enrolmentPhoto = enrolmentPhoto,
        boardingPass = boardingPass,
        processReport = processReport,
        matchReport = matchReport,
        documentReaderReport = documentReaderReport
    )
    val result = enrolment.buildSubject(params)
}

Warning

Because the Subject model might become a big object it may cause the parcelable too large exception. For this reason, it’s not parcelable. If you need to transform the Subject model in a parcelable object, you can use the helper method toSubjectParcelable() and after using it on a Bundle, you should reconvert it to a Subject using the method toSubject().

let parameters = BuildSubjectParameters(
    documentData: documentData,
    documentImage: documentImage,
    enrolmentImage: enrolmentImage,
    boardingPass: EnrolmentData.shared.boardingPass,
    documentReaderReport: EnrolmentData.shared.documentReaderReport,
    biometricFaceCaptureReport: EnrolmentData.shared.biometricMatchReport,
    matchReport: EnrolmentData.shared.matchReport,
    language: Locale.current
)

guard let vco = self.view as? UIViewController else {
    return
}

self.enrolment.buildSubject(
    parameters: parameters,
    viewController: vco) { [weak self] result in
    switch result {
    case .success(let subject):
        self?.addSubject(subject: subject)
    case .failure:
        self?.view?.onBuildSubjectError()
    }
}

The SDK does not provide any UI solutions for subject representation. You should build your own layouts and use the information that is relevant for your mobile solution.

These are all the subject methods:

/**
* Builds a [Subject] instance with the given [params].
*
* @param params [BuildSubjectParameters] that contains the necessary data to build the subject.
* @return a [Result] with a [Subject] or [SubjectBuilderError].
*/
fun buildSubject(
    activity: Activity,
    params: BuildSubjectParameters
): Result<Subject, SubjectBuilderError>

/**
 * Adds a [Subject].
 *
 * @param context Context
 * @param params contains instance from [Subject] that will be added in the server.
 * @param resultLauncher [ActivityResultLauncher<Intent>] fragment or activity that will handle the results .
 */
fun addSubject(
    context: Context,
    params: AddSubjectParameters,
    resultLauncher: ActivityResultLauncher<Intent>
)
/// Build a Subject from document data. Can, optionally, receive a boarding pass.
/// - Parameters:
///   - parameters: Parameters for the Building Subject flow.
///   - viewController: View controller base from when to present required SDK view controllers.
///   - completionHandler: The completion handler to call when the build subject operation is finished.
///     This completion handler takes the following parameter:
///
///     Result<Subject, SubjectBuilderError>
///     Where `Subject` contains  the subject information and `SubjectError`
///     the possible errors that may occur during the process.
func buildSubject(parameters: BuildSubjectParameters, viewController: UIViewController, completionHandler: @escaping (Result<Subject, SubjectError>) -> Void)

/// Saves a Subect to server.
/// - Parameters:
///   - parameters: Parameteres for the Add Subject flow.
///   - viewController: View controller base from when to present required SDK view controllers.
///   - completionHandler: The completion handler to call when the add subject operation is finished.
///     This completion handler takes the following parameter:
///
///     Result<Void, SubjectError>
///     Where `SubjectError` contains the possible errors that may occur during the process.
func addSubject(parameters: AddSubjectParameters, viewController: UIViewController, completionHandler: @escaping (Result<Void, SubjectError>) -> Void)

Adding a Subject required the AddSubjectParameters which have the following structure:

data class AddSubjectParameters(
    val subject: Subject,
    val formAnswer: FormAnswer? = null,
)
public struct AddSubjectParameters {
    public let subject: Subject

    public init(subject: Subject)
}

Handle Result

You can get the result by registering the callback. In this instance, the subjectId of the created Subject will be returned in the case of a success.

interface OnAddSubjectCompletion {
    fun onAddSubjectSuccess(subjectId: String)
    fun onAddSubjectError(subjectError: SubjectError)
}

self.enrolment.addSubject(
    parameters: AddSubjectParameters(subject: subject),
    viewController: vco) { [weak self] result in

    switch result {
    case .success:
        print("Add Subject: Success!")
    case .failure(let error):
        print(error.featureError.publicMessage)
    }
}

The SubjectError has the following structure:

data class SubjectError(
    val userCanceled: Boolean,
    val featureError: FeatureError?
)
public class SubjectError: Error {
    public var userCanceled: Bool
    public var termsAndConditionsAccepted: Bool
    public var featureError: FeatureError
}

Biometric

The biometrics attribute on the Subject is a list of biometric data. This biometric data contains its source, a base64 of the image and it's format, which you can get the image bitmap by calling the method getBiometricImage(), type and position. Here is the structure of the Biometric data:

data class Biometric(
    val source: BiometricSource,
    val data: String,
)
public struct Biometric {
    public let type: BiometricType
    public let format: BiometricFormat
    public let position: BiometricTypePosition
    public let source: BiometricSource
    public let data: Data
    public let photo: UIImage?
}

The BiometricFormat will specify the format for the data string like so:

/**
* Biometric format type for both Face and Document captures
*/
enum class BiometricFormat {
    Unknown, Jpg, Png
}
public enum BiometricFormat: String {
    case unknown = "Unknown"
    case jpg = "Jpg"
    case png = "Png"
}

The BiometricSource is a enum with the source of the biometric photo and will have the following structure:

/**
* Source of the provided photo. Identifies how it was obtained.
*/
enum class BiometricSource {
    Unknown,
    Face,
    Ocr,
    Chip
}
public enum BiometricSource: String {
    case documentChip = "Chip"
    case documentOCR = "Ocr"
    case faceCapture = "Face"
}

The BiometricType defines the capture type of the biometric:

/**
 * Biometrics type for the Face capture
 */
enum class BiometricType {
    Unknown, Enrolment
}
  public enum BiometricType: String {
    case unknown = "Unknown"
    case enrolment = "Enrolment"
}

The BiometricPosition is something only present on face captured Biometrics

    /**
    * Biometric position of the source in question
    */
    enum class BiometricPosition {
        Unknown, Face
    }
public enum BiometricTypePosition: String {
    case unknown = "Unknown"
    case face = "Face"
}

Subject Status

After adding a Subject, the id will be returned. This id can be used to get the SubjectStatus which will contain information about the current status of a given Subject.

    data class SubjectStatus(val subjectId: String?, val status: Status, val detail: String?) {

        enum class Status {
            Pending,
            Validating,
            Invalid,
            Enrolled,
            Expired
        }
    }
public struct SubjectStatus {
    public let id: String
    public let status: Status
}

The SubjectStatus is accessible through the Enrolment by calling one these methods below and registering the OnSubjectStatusResult callback.

/**
 * Gets the [Subject] status for the given subjectId.
 *
 * @param subjectId of the specified [Subject]
 * @param listener to get the result back
 */
fun getSubjectStatus(
    subjectId: String,
    listener: OnSubjectStatusResult
)

/**
 * Gets a list of [SubjectStatus] for this device 
 *
 * @param listener callback to get the [SubjectStatus] back
 */
fun getAllSubjectStatus(
    listener: OnSubjectStatusResult
)

/**
 * Callback for Success and Error when fetching a single or multiple [SubjectStatus]
 */
interface OnSubjectStatusResult {

    /**
     * Success callback
     *
     * @param status contains a list of [SubjectStatus] returning the [Subject] id and it's [SubjectStatus.Status]
     */
    fun onStatus(status: List<SubjectStatus>)

    /**
     * Error callback
     */
    fun onError(error: String)
}
/// Get all status from server.
///     /// - Parameters:
///   - completionHandler: The completion handler to call when the get status operation is finished.
///     This completion handler takes the following parameter:
///
///     Result<[SubjectStatus], SubjectError>
///     Where `SubjectError` contains the possible errors that may occur during the process.
func getStatus(completionHandler: @escaping (Result<[SubjectStatus], SubjectError>) -> Void)

/// Get  status by id from server.
/// - Parameters:
///   - subjectId: id to retrive
///   - completionHandler: The completion handler to call when the get status operation is finished.
///     This completion handler takes the following parameter:
///
///     Result<[SubjectStatus], SubjectError>
///     Where `SubjectError` contains the possible errors that may occur during the process.

func getStatus(subjectId:String, completionHandler: @escaping (Result<SubjectStatus, SubjectError>) -> Void)

SubjectCustomViews

The SDK provides default UI solutions for the Subject Management feature flow, as shown in the following images:

Subject Management Example

You can also apply your app’s colors and fonts to these layout solutions, to keep your brand’s image consistent. Check Customization tab to learn more about branding of each view.

@Parcelize
class SubjectCustomViews(
    val loadingView: Class<out ICustomSubject.LoadingView>? = null
) : Parcelable
You can use your own custom views in the subject functionality. Your view must implement the SDK view interfaces. For example, if you want to add a custom loading view, your view class must implement the ICustomSubject.LoadingView interface.

public class EnrolmentViewRegister {
    ...

    // MARK: - Subject Operations
    public func registerSubjectLoadingOverlayView(_ viewType: SubjectLoadingOverlayViewType)
    ...
}
You can use your own custom views in the subject functionality. Your view must implement the SDK view protocols. For example, if you want to add a custom loading view, your view class must implement the ICustomSubject.LoadingView interface.