PIN, face and liveness verification 

This page explains how to integrate PIN and face liveness verification using PinManager and FaceManager.

What This Covers 

  • creating and updating PIN with PinManager
  • verifying PIN (PinUseCase.verify) for authentication challenges
  • creating face verification screens with FaceManager
  • passing liveness settings through FaceConfiguration
  • handling notification-driven authentication prompts through RequestAuthenticationDelegate
  • mapping and handling SDK errors (PinManagerError, FaceManagerError)

API Surface 

PIN APIs 

  • mobileID.pinManager
  • PinManager.isPinSet: Bool
  • PinManager.makePinViewController(for:onPinPhaseFinishedHandler:) -> PinViewController?
  • PinUseCase: .set, .verify
  • PinPhase: .verification, .creationFirstStep, .creationSecondStep
  • PinViewController
  • PinManagerError
  • KeypadType: .default, .custom(withProcessButton:)

Face/Liveness APIs 

  • mobileID.faceManager
  • FaceManager.isFaceSet() throws -> Bool
  • FaceManager.makeFaceVerificationViewController(for:with:handler:) async throws -> FaceViewController
  • FaceConfiguration
  • FaceViewController
  • FaceManagerError
  • LivenessSettings and enums:
    • LivenessSettings.LivenessType
    • LivenessSettings.SecurityLevel

Authentication Counterparts (Notification Flows) 

  • MobileIDNotification.accept(using:delegate:)
  • RequestAuthenticationDelegate
  • AuthenticationRequest

When request authentication is started by the SDK (for example from notifications), the SDK provides either PinViewController or FaceViewController to your delegate, and your app is responsible for presenting/dismissing those screens.

End-To-End Flow 

diagram

PIN Integration 

1. Build The PIN Screen 

Swift
1import MobileIDCore
2import UIKit
3
4@MainActor
5func presentPinSetup(mobileID: MobileID, from navigationController: UINavigationController) {
6 guard let pinVC = mobileID.pinManager.makePinViewController(for: .set, onPinPhaseFinishedHandler: { result in
7 switch result {
8 case .success(.verification):
9 // Existing PIN was verified (only happens when updating an already-set PIN).
10 break
11 case .success(.creationFirstStep):
12 // First PIN entry accepted, waiting for confirmation entry.
13 break
14 case .success(.creationSecondStep):
15 // PIN creation/update completed.
16 break
17
18 case .failure(let error):
19 // Show UI message and recover accordingly.
20 print("PIN flow error: \(error)")
21 }
22 }) else {
23 // Expected only if useCase is .verify and no PIN is set.
24 return
25 }
26
27 pinVC.keypadType = .custom(withProcessButton: true)
28 pinVC.feedbackView = {
29 // Your feedback view
30 }
31
32 pinVC.onPinChange = { phase, digits in
33 print("PIN phase=\(phase), digits=\(digits)")
34 }
35
36 navigationController.pushViewController(pinVC, animated: true)
37}

2. Trigger Processing And Restart Correctly 

  • If you use .custom(withProcessButton: true), the built-in process button triggers processPin().
  • If you use another keypad mode, call await pinViewController.processPin() when your UX decides submission is ready.
  • After a handled success/failure that should continue the same PIN flow, call await pinViewController.restart() before next user interaction.

Face + Liveness Integration 

Prerequisites 

  • Include and link the face matching plugin module.
  • Ensure captureSDKConfiguration is present in your MobileIDConfiguration.
  • Ensure the user completed face enrollment if your flow requires a stored template.

1. Check Readiness 

Swift
1let hasFace = try mobileID.faceManager.isFaceSet()

isFaceSet() can throw FaceManagerError.faceMatchingModuleNotFound if the face module is not linked correctly.

2. Create And Present Face Verification 

Swift
1import MobileIDCore
2import MobileIDTheme
3import UIKit
4
5@MainActor
6func presentFaceVerification(
7 mobileID: MobileID,
8 navigationController: UINavigationController,
9 theme: ThemeConfiguration,
10 livenessSettings: LivenessSettings,
11 template: Data?
12) async {
13 let config = FaceConfiguration(
14 title: "Verify your identity",
15 livenessSettings: livenessSettings,
16 theme: theme
17 )
18
19 let handler: (Result<UIImage, Error>) -> Void = { result in
20 switch result {
21 case .success(let capturedImage):
22 print("Face challenge passed, image size=\(capturedImage.size)")
23 case .failure(let error):
24 print("Face challenge failed: \(error)")
25 }
26 }
27
28 do {
29 let faceVC = try await mobileID.faceManager.makeFaceVerificationViewController(
30 for: config,
31 with: template,
32 handler: handler
33 )
34 navigationController.pushViewController(faceVC, animated: true)
35 } catch {
36 print("Unable to create face verification screen: \(error)")
37 }
38}

Notes:

  • If template is nil, SDK uses the portrait template saved during face enrollment.
  • For online authentication requests, the SDK internally uses captureSDKConfiguration.settings.onlineAuthentication when it orchestrates face prompts.

Liveness Settings Reference 

LivenessSettings controls challenge behavior:

  • livenessType: .active, .passive, .passiveVideo, .noLiveness
  • targetsNumber: relevant for .active (valid range in SDK: 1...3)
  • securityLevel: .low, .medium, .high
  • numberOfAttempts: attempts before failure
  • faceAcquisitionTimeout: milliseconds (>= 5000)
  • faceMatchingThreshold: when matching score is required (SDK validation range: 2500...5000)
  • isIdleTimerDisabled: idle timer behavior for challenge UX

Use CustomLivenessSettings (through CaptureSDKConfiguration) when overriding defaults per scenario.

Notification-Driven Authentication (PIN/Face Prompts) 

For request acceptance flows, use RequestAuthenticationDelegate.

Swift
1import MobileIDCore
2import UIKit
3
4final class RequestAuthDelegate: RequestAuthenticationDelegate {
5 private weak var navigationController: UINavigationController?
6
7 init(navigationController: UINavigationController) {
8 self.navigationController = navigationController
9 }
10
11 func notification(_ request: AuthenticationRequest, promptedForAuthentication authenticationPrompt: PinViewController) {
12 navigationController?.pushViewController(authenticationPrompt, animated: true)
13 }
14
15 func notification(_ request: AuthenticationRequest, promptedForAuthentication authenticationPrompt: FaceViewController) {
16 navigationController?.pushViewController(authenticationPrompt, animated: true)
17 }
18
19 func didFinishAuthentication(of request: AuthenticationRequest) {
20 navigationController?.popToRootViewController(animated: true)
21 }
22
23 func notificationDidBecomeBusy(_ request: AuthenticationRequest, expectedInterval: NavigatorBusyStateInterval) {
24 // Show loader.
25 }
26
27 func notificationDidBecomeIdle(_ request: AuthenticationRequest) {
28 // Hide loader.
29 }
30}

Error Handling Cheat Sheet 

PIN (PinManagerError) 

  • pinInvalid: wrong PIN entered
  • pinValuesDoNotMatch: PIN confirmation mismatch
  • proposedPINDoesNotMeetRequirements: blacklisted/invalid PIN policy
  • pinLengthExceedsBounds: entered PIN outside allowed policy bounds
  • pinDigitIgnoredDueToPinLengthBounds: extra digit ignored
  • incorrectPinPolicyConfiguration: wallet/policy configuration issue
  • notPreparedForInteraction: flow not restarted/prepared before user interaction
  • couldNotConnectToServer: network issue after too many failed attempts
  • verificationTemporaryBlocked(backoffTimeInSeconds:): temporary lockout; retry after backoff

Face (FaceManagerError) 

  • faceMatchingNotConfigured: missing captureSDKConfiguration
  • faceMatchingModuleNotFound: missing/unlinked face matching module

Implementation Guidance 

  • Use .set for first-time PIN creation and for PIN updates (update starts with verification when PIN already exists).
  • Expect makePinViewController(for: .verify, ...) to return nil if PIN is not set yet.
  • Keep PIN/face prompt presentation centralized in one coordinator to avoid duplicate navigation logic.
  • Always provide user-facing recovery for lockout/backoff and configuration errors.
  • Keep liveness customization server/environment aligned with your risk policy and UX goals.
  • MobileID
  • MobileIDNotification
  • RequestAuthenticationManager
  • CaptureSDKConfiguration
  • CaptureSDKSettings
  • CustomLivenessSettings