# iOS Liveness SDK

[Get your Public Merchant Key](https://doc.youverify.co/getting-started/getting-your-public-merchant-key)

**Youverify Liveness SDK** is the recommended iOS SDK for integrating real-time liveness detection into your SwiftUI-based applications. It supports both passive and active liveness detection, offering a modern and secure way to verify user presence. This SDK is ideal for developers targeting iOS 13 and later, with additional features available on iOS 15 and above.

### Features

* **Passive Liveness Detection**: Analyzes facial features (e.g., blinking, skin texture) to confirm liveness without user interaction. Available on iOS 15+.
* **Active Liveness Detection**: Requires users to perform specific actions (e.g., head movements, answering questions) to verify liveness. Available on iOS 14+.
* Built with **SwiftUI**, aligning with modern iOS development practices.

### Installation

Get started with the SDK in just a few steps:

1. **Add to Your Podfile**\
   Open your project’s `Podfile` and include:

   ```ruby
   pod 'YouverifyLivenessSDK'
   pod 'MediaPipeTaskVision'
   ```
2. **Install Dependencies**\
   Run this command in your terminal:

   ```sh
   pod install
   ```
3. **Open Your Project**\
   Launch the generated `.xcworkspace` file in Xcode.

   **Project Configuration**

   To prevent linker errors, add the following post-install script to your application's Podfile, making sure to replace `"YourAppTarget"` with the correct target name for your app.

post\_install do |installer|\
target\_name = "YourAppTarget"

```
# Get the paths to the xcconfig files
debug_xcconfig_path = "#{installer.pods_project.project_dir}/Target Support Files/Pods-#{target_name}/Pods-#{target_name}.debug.xcconfig"
release_xcconfig_path = "#{installer.pods_project.project_dir}/Target Support Files/Pods-#{target_name}/Pods-#{target_name}.release.xcconfig"

# Modify the debug .xcconfig file if it exists
if File.exist?(debug_xcconfig_path)
  puts "Modifying debug .xcconfig file: #{debug_xcconfig_path}"

  # Read the debug .xcconfig file
  debug_xcconfig = File.read(debug_xcconfig_path)

  # Remove framework flags from OTHER_LDFLAGS
  debug_xcconfig.gsub!(/-framework\s+"MediaPipeTasksCommon"/, '')
  debug_xcconfig.gsub!(/-framework\s+"MediaPipeTasksVision"/, '')

  # Remove -force_load from OTHER_LDFLAGS for iphoneos and iphonesimulator
  debug_xcconfig.gsub!(/-force_load\s+"?[^"]*libMediaPipeTasksCommon.*\.a"?/, '')

  # Write the modified debug .xcconfig file
  File.write(debug_xcconfig_path, debug_xcconfig)
  puts "Modified debug .xcconfig file."
else
  puts "Debug .xcconfig file not found at #{debug_xcconfig_path}"
end

# Modify the release .xcconfig file if it exists
if File.exist?(release_xcconfig_path)
  puts "Modifying release .xcconfig file: #{release_xcconfig_path}"

  # Read the release .xcconfig file
  release_xcconfig = File.read(release_xcconfig_path)

  # Remove framework flags from OTHER_LDFLAGS
  release_xcconfig.gsub!(/-framework\s+"MediaPipeTasksCommon"/, '')
  release_xcconfig.gsub!(/-framework\s+"MediaPipeTasksVision"/, '')

  # Remove -force_load from OTHER_LDFLAGS for iphoneos and iphonesimulator
  release_xcconfig.gsub!(/-force_load\s+"?[^"]*libMediaPipeTasksCommon.*\.a"?/, '')

  # Write the modified release .xcconfig file
  File.write(release_xcconfig_path, release_xcconfig)
  puts "Modified release .xcconfig file."
else
  puts "Release .xcconfig file not found at #{release_xcconfig_path}"
end
```

end

### Usage

#### 1. Import the Package

To use the SDK in your SwiftUI view, import it at the top of your file:

```swift
import YVLivenessSDK
```

#### 2. Initialize an Instance

Create an instance of the SDK as a private variable within your SwiftUI view. Pass in the necessary configuration options during initialization:

```swift
private var yvSDK = YVLiveness(
    publicKey: "YOUR_PUBLIC_KEY",
    user: SDKUser(firstName: "Ugochukwu"),
    onSuccess: { data in print("The data returned is \(data.faceImage)") },
    branding: Branding(color: "#4287f5")
)
```

* The `publicKey` can be retrieved dynamically (e.g., from an environment variable), as shown in the working code with `Environment.shared.value(forKey: "API_KEY") ?? ""`.
* Optional parameters like `onFailure`, `lastName`, and `email` can be included if needed (see Configuration Options).

#### 3. Start the Liveness Process

Trigger the liveness detection process by calling `startSDK()` on your SDK instance. Pass an array of tasks to specify the liveness checks to perform:

**Complete the Circle**

```swift
yvSDK.startSDK(tasks: [TaskProperties(task: YVTask.completeTheCircle(CompleteTheCircleTask(difficulty: .medium)))])
```

**Yes or No**

```swift
yvSDK.startSDK(tasks: [TaskProperties(task: YVTask.yesOrNo(YesOrNoTask(questions: [
    YesOrNoQuestion(question: "Is Nigeria a country?", answer: true, errorMessage: "That's the wrong answer")
])))])
```

**Motions**

```swift
yvSDK.startSDK(tasks: [TaskProperties(task: YVTask.motions(MotionsTaskClass(maxNods: 5, maxBlinks: 5)))])
```

**Passive Liveness**

```swift
yvSDK.startSDK(tasks: [TaskProperties(task: YVTask.passive)])
```

See the Tasks section for detailed task options.

#### 4. Add the SDK View

Include the SDK’s view in your SwiftUI view hierarchy to display the liveness detection interface:

```swift
YVLivenessView(sdk: yvSDK)
```

Unlike the previous version, this view should always be present in the view hierarchy (not conditionally rendered). The SDK internally manages when to show the liveness interface based on `startSDK()` calls.

### Configuration Options

The following options can be passed during initialization of `YVLiveness`:

| **Option**           | **Type** | **Required** | **Description**                          | **Default Value** | **Possible Values** |
| -------------------- | -------- | ------------ | ---------------------------------------- | ----------------- | ------------------- |
| `publicKey`          | String   | Yes          | Your API key for authentication          | -                 | Any string          |
| `user`               | Class    | Yes          | User details (requires `firstName`)      | -                 | See below           |
| `user.firstName`     | String   | Yes          | User’s first name                        | -                 | Any string          |
| `user.lastName`      | String   | No           | User’s last name                         | `nil`             | Any string          |
| `user.email`         | String   | No           | User’s email                             | `nil`             | Any string          |
| `onSuccess`          | Function | No           | Callback on success with liveness data   | `nil`             | Any function        |
| `onFailure`          | Function | No           | Callback on failure with error data      | `nil`             | Any function        |
| `sandboxEnvironment` | Boolean  | No           | Toggle sandbox mode for testing          | `true`            | `true`, `false`     |
| `tasks`              | Array    | No           | Define liveness tasks for initialization | `nil`             | See Tasks           |
| `branding`           | Class    | No           | Customize SDK appearance (e.g., color)   | `nil`             | color               |

### Tasks

Tasks are interactive challenges or passive checks to confirm liveness. Specify them when calling `startSDK()`. Below are the available tasks:

#### Complete The Circle

Users trace a circle with head movements.

| **Option**   | **Type**       | **Required** | **Description**             | **Default** | **Possible Values**         |
| ------------ | -------------- | ------------ | --------------------------- | ----------- | --------------------------- |
| `difficulty` | TaskDifficulty | No           | Task difficulty             | `.medium`   | `.easy`, `.medium`, `.hard` |
| `timeout`    | Number         | No           | Time (ms) before task fails | `nil`       | Any milliseconds            |

Example:

```swift
TaskProperties(task: YVTask.completeTheCircle(CompleteTheCircleTask(difficulty: .medium)))
```

#### Yes or No

Users answer questions by tilting their head (right = yes, left = no).

| **Option**               | **Type**       | **Required** | **Description**             | **Default** | **Possible Values**         |
| ------------------------ | -------------- | ------------ | --------------------------- | ----------- | --------------------------- |
| `difficulty`             | TaskDifficulty | No           | Task difficulty             | `.medium`   | `.easy`, `.medium`, `.hard` |
| `timeout`                | Number         | No           | Time (ms) before task fails | `nil`       | Any milliseconds            |
| `questions`              | Array          | Yes          | List of yes/no questions    | -           | See below                   |
| `questions.question`     | String         | Yes          | Yes/no question             | -           | Any string                  |
| `questions.answer`       | Bool           | Yes          | Correct answer              | -           | `true`, `false`             |
| `questions.errorMessage` | String         | No           | Message on wrong answer     | `nil`       | Any string                  |

Example:

```swift
TaskProperties(task: YVTask.yesOrNo(YesOrNoTask(questions: [
    YesOrNoQuestion(question: "Is Nigeria a country?", answer: true, errorMessage: "That's the wrong answer")
])))
```

#### Motions

Users perform nods, blinks, or mouth movements.

| **Option**   | **Type**       | **Required** | **Description**             | **Default** | **Possible Values**         |
| ------------ | -------------- | ------------ | --------------------------- | ----------- | --------------------------- |
| `difficulty` | TaskDifficulty | No           | Task difficulty             | `.medium`   | `.easy`, `.medium`, `.hard` |
| `timeout`    | Number         | No           | Time (ms) before task fails | `nil`       | Any milliseconds            |
| `maxNods`    | Int            | No           | Max nods to perform         | `5`         | Any number                  |
| `maxBlinks`  | Int            | No           | Max blinks to perform       | `5`         | Any number                  |

Example:

```swift
TaskProperties(task: YVTask.motions(MotionsTaskClass(maxNods: 5, maxBlinks: 5)))
```

#### Passive Liveness

Analyzes facial features without user interaction (no additional parameters required).

Example:

```swift
TaskProperties(task: YVTask.passive)
```

### Liveness Data

The `onSuccess` callback returns the following data:

| **Field**      | **Type** | **Description**                         |
| -------------- | -------- | --------------------------------------- |
| `faceImage`    | String   | User’s face image                       |
| `livenessClip` | String   | Video of liveness check (if applicable) |
| `passed`       | Bool     | True if passed, false if failed         |
| `metadata`     | Any      | Metadata from initialization            |

The `onFailure` callback (if provided) returns error details as a string or object.

### Full Example

Here’s the complete SwiftUI view from the working code, demonstrating the SDK integration:

```swift
import SwiftUI
import YVLivenessSDK

struct ContentView: View {
    private var yvSDK = YVLiveness(
        publicKey: Environment.shared.value(forKey: "API_KEY") ?? "",
        user: SDKUser(firstName: "Ugochukwu"),
        onSuccess: { data in
            print("The data returned is \(data.faceImage)")
        },
        branding: Branding(color: "#4287f5")
    )
    let TIMEOUT = 20.0
    
    var body: some View {
        ZStack {
            VStack(spacing: 10) {
                Button(action: {
                    yvSDK.startSDK(tasks: [TaskProperties(task: YVTask.completeTheCircle(CompleteTheCircleTask(difficulty: .medium)))])
                }, label: {
                    Text("Complete the Circle")
                })
                
                Button(action: {
                    yvSDK.startSDK(tasks: [TaskProperties(task:
                        YVTask.yesOrNo(YesOrNoTask(questions: [
                            YesOrNoQuestion(question: "Is Nigeria a country?", answer: true, errorMessage: "That's the wrong answer")
                        ])))
                    ])
                }, label: {
                    Text("Yes or No")
                })
                
                Button(action: {
                    yvSDK.startSDK(tasks: [TaskProperties(task:
                        YVTask.motions(MotionsTaskClass(maxNods: 5, maxBlinks: 5)))
                    ])
                }, label: {
                    Text("Motions")
                })
                
                Button(action: {
                    yvSDK.startSDK(tasks: [TaskProperties(task: YVTask.passive)])
                }, label: {
                    Text("Passive Liveness")
                })
            }
            .padding()
            
            YVLivenessView(
                sdk: yvSDK
            )
        }
    }
}
```

### Credits

This SDK is developed and maintained solely by [Youverify](https://youverify.co)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://doc.youverify.co/know-your-customer-services-kyc/sdk/ios-sdk/ios-liveness-sdk.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
