# Face Match

The Face Match module compares the user's selfie against their ID photo, their NFC chip photo, or both in a 3-way match. It then returns a match decision and confidence score. Matching is performed on the server. The module can also report whether the face already belongs to an existing user.

For an overview of this module and how it works, see [Face Match](https://developer.incode.com/docs/face-match).

How you use this module depends on your integration pattern. When the app defines the steps in code, you add the module to an `IncdOnboardingFlowConfiguration` as shown below; when the flow is defined in Dashboard, you reference it by ID. See [Integration Approaches](https://developer.incode.com/docs/ios-flow-configuration).

**Availability:** All variants.

## Add Face Match

1. Add an [ID Capture](https://developer.incode.com/docs/module-id-scan) or [QR Scan](https://developer.incode.com/docs/module-qr-scan) module, and a [Selfie](https://developer.incode.com/docs/module-selfie) module, before Face Match. Face Match depends on these earlier capture steps so there are two faces to compare.
2. Add the module with `addFaceMatch()`. Every parameter has a default, so a bare call uses the module defaults.
   ```swift
   // Default configuration
   flowConfig.addFaceMatch()

   // Custom configuration
   flowConfig.addFaceMatch(
       matchType: .idSelfie,     // .idSelfie (default), .nfcSelfie, .nfc3Way
       uiFlavor: .full,          // .full (default) or .compact
       idCategory: .primary,     // variadic; .primary and/or .secondary
       showUserExists: nil,      // defaults to true
       showLivenessStatus: nil   // defaults to true on v1, false on v2
   )
   ```

Face Match ships both a v1 (UIKit View) and a v2 (SwiftUI-style) UI.

### Example

The example below adds ID Capture and Selfie before Face Match, configures an ID-vs-selfie comparison, starts the flow, and reads the result in `onFaceMatchCompleted(_:)`.

```swift
let flow = IncdOnboardingFlowConfiguration()
flow.addIdScan(scanStep: .both)
flow.addSelfieScan()
flow.addFaceMatch(matchType: .idSelfie, idCategory: .primary)

IncdOnboardingManager.shared.startOnboarding(
    sessionConfig: IncdOnboardingSessionConfiguration(token: "<SESSION_TOKEN>"),
    flowConfig: flow,
    delegate: self
)

// In your IncdOnboardingDelegate:
func onFaceMatchCompleted(_ result: FaceMatchResult) {
    // Face match completed — inspect result.faceMatched, result.confidence, etc.
}
```

## Configuration Options

Configure the module with `addFaceMatch()`.

| Option               | Type             | Description                                                                                                                      |
| -------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `matchType`          | `FaceMatchType?` | Selects what to compare: `.idSelfie` (default), `.nfcSelfie`, or `.nfc3Way`. The NFC match types require captured NFC chip data. |
| `uiFlavor`           | `UIFlavor?`      | `.full` (default) or `.compact`. `.compact` is the reduced UI that disables the face-match animation.                            |
| `idCategory`         | `IDCategory...`  | Sets one or more IDs to match against: `.primary` (default), `.secondary`, or both.                                              |
| `showUserExists`     | `Bool?`          | Shows or hides the label indicating the user already exists. Default is `true`.                                                  |
| `showLivenessStatus` | `Bool?`          | Shows or hides the liveness result label. Default is `true` on UI v1 and `false` on UI v2.                                       |

The match type and the thresholds used to decide a pass or fail also depend on your Incode Flow or Workflow configuration, not just these options.

## Result

Face Match delivers a `FaceMatchResult` to the `onFaceMatchCompleted(_:)` callback on `IncdOnboardingDelegate`:

```swift
func onFaceMatchCompleted(_ result: FaceMatchResult)
```

`FaceMatchResult` fields:

- `faceMatched: Bool?`: If the faces matched.
- `confidence: Float?`: Recognition confidence between the selfie and the front ID photo. `nil` when no front ID was uploaded.
- `secondIdConfidence: Float?`: Recognition confidence against the second ID photo.
- `nfcSelfieConfidence: Float?`: Recognition confidence between the selfie and the NFC chip photo.
- `nfcIdConfidence: Float?`: Recognition confidence between the front ID photo and the NFC chip photo.
- `nameMatched: Bool?`: `true` if the existing user's name matches the current user's name (use together with `existingUser`); otherwise, `false`.
- `existingUser: Bool?`: `true` if the user already exists in the database; otherwise, `false`.
- `existingInterviewId: String?`: The interview ID of the existing user, if one exists; otherwise, `nil`.
- `idCategories: Set<IDCategory>`: The list of `IDCategory` values used in the face matching process.
- `error: IncdError?`: Set when the module produced an error; otherwise, `nil`.

## Headless

The headless entry point takes the same options as the module. `idCategory` is variadic; pass zero or more `IDCategory` values. `interviewId` is optional; pass a value to target a specific user session, or omit it to use the default.

```swift
public func faceMatch(
    matchType: FaceMatchType? = nil,
    idCategory: IDCategory...,
    interviewId: String? = nil,
    completion: @escaping (_ result: FaceMatchResult) -> Void
)
```

```swift
// interviewId is optional and omitted here; idCategory is variadic
IncdOnboardingManager.shared.faceMatch(matchType: .idSelfie, idCategory: .primary) { result in
    // FaceMatchResult
}
```

Requires an active session.
