---
title: Step 3. Develop the client app
description: Integrating your application with PingOne Protect enables you to perform risk evaluations during your customer's authentication flow.
component: sdks
version: latest
page_id: sdks:davinci:use-cases/pingone-protect/03-app
canonical_url: https://docs.pingidentity.com/sdks/latest/davinci/use-cases/pingone-protect/03-app.html
section_ids:
  start: Initialize data collection
  direct: Direct initialization using the Protect interface
  module: Use the ProtectLifecycle module for Android or iOS
  on-demand: On-demand initialization on receipt of ProtectCollector
  pause-resume: Pause and resume behavioral data capture
  return-data-davinci-flow: Return captured data to a DaVinci flow
---

# Step 3. Develop the client app

Integrating your application with PingOne Protect enables you to perform risk evaluations during your customer's authentication flow.

Add code for the following tasks to fully integrate with PingOne Protect:

1. [Initialize data collection](#start)

2. [Pause and resume behavioral data capture](#pause-resume)

3. [Return captured data to a DaVinci flow](#return-data-davinci-flow)

## Initialize data collection

The earlier you can initialize data collection, the more data it can collect to make a risk evaluation.

Your client application can manually initialize data collection, and must provide the configuration to control the PingOne Signals SDK.

There are three main methods for initializing data collection:

1. [Direct initialization using the Protect interface](#direct)

2. [Use the ProtectLifecycle module for Android or iOS](#module)

3. [On-demand initialization on receipt of ProtectCollector](#on-demand)

### Direct initialization using the Protect interface

The DaVinci Client allows you to initialize data collection directly using the `Protect` interface. This provides maximum flexibility in how the collection operates.

To directly initialize data collection using the `Protect` interface, complete these steps:

1. Add a configuration object to your code that defines the property values for data collection.

   The available properties are as follows:

   * Android

   * iOS

   * JavaScript

   | **Parameter**                | **Description**                                                                                                                            |
   | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
   | `envID`                      | Required. Your PingOne environment identifier.For example, `3072206d-c6ce-ch15-m0nd-f87e972c7cc3`                                          |
   | `deviceAttributesToIgnore`   | Optional. A list of device attributes to ignore when collecting device signals.For example, `AUDIO_OUTPUT_DEVICES` or `IS_ACCEPT_COOKIES`. |
   | `isBehavioralDataCollection` | When `true`, collect behavioral data.Default is `true`.                                                                                    |
   | `isConsoleLogEnabled`        | When `true`, output SDK log messages in the developer console.Default is `false`.                                                          |
   | `isLazyMetadata`             | When `true`, calculate metadata on demand rather than automatically after calling `start`.Default is `false`.                              |
   | `customHost`                 | Optional. Specify a custom host for the Protect API, which can be useful in specific deployment scenarios.                                 |

   | **Parameter**                | **Description**                                                                                                                            |
   | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
   | `envID`                      | Required. Your PingOne environment identifier.For example, `3072206d-c6ce-ch15-m0nd-f87e972c7cc3`                                          |
   | `deviceAttributesToIgnore`   | Optional. A list of device attributes to ignore when collecting device signals.For example, `AUDIO_OUTPUT_DEVICES` or `IS_ACCEPT_COOKIES`. |
   | `isBehavioralDataCollection` | When `true`, collect behavioral data.Default is `true`.                                                                                    |
   | `isConsoleLogEnabled`        | When `true`, output SDK log messages in the developer console.Default is `false`.                                                          |
   | `isLazyMetadata`             | When `true`, calculate metadata on demand rather than automatically after calling `start`.Default is `false`.                              |
   | `customHost`                 | Optional. Specify a custom host for the Protect API, which can be useful in specific deployment scenarios.                                 |

   | **Parameter**                   | **Description**                                                                                                                                      |
   | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
   | `envID`                         | Required. Your PingOne environment identifier.For example, `3072206d-c6ce-ch15-m0nd-f87e972c7cc3`                                                    |
   | `deviceAttributesToIgnore`      | Optional. A list of device attributes to ignore when collecting device signals.For example, `AUDIO_OUTPUT_DEVICES` or `IS_ACCEPT_COOKIES`.           |
   | `behavioralDataCollection`      | When `true`, collect behavioral data.Default is `true`.                                                                                              |
   | `hubUrl`                        | Optional. The iframe URL to use for cross-storage device IDs.                                                                                        |
   | `disableHub`                    | When `true`, the client stores device data in the browser's `localStorage` only.When `false` the client uses an iframe.Default is `false`.           |
   | `disableTags`                   | When `true`, the client does not collect tag data.Tags are used to record the pages the user visited, forming a browsing history.Default is `false`. |
   | `externalIdentifiers`           | Optional. A list of custom identifiers that are associated with the device entity in PingOne Protect.                                                |
   | `waitForWindowLoad`             | When `true`, initialize the SDK on the `load` event, instead of the `DOMContentLoaded` event.Default is `true`.                                      |
   | `universalDeviceIdentification` | Optional. When `true`, device data in the payload returned to the server is provided as a signed JWT.                                                |

   Example code:

   * Android

   * iOS

   * JavaScript

   ```kotlin
   Protect.config {
       isBehavioralDataCollection = true
       isLazyMetadata = true
       envId = "3072206d-c6ce-ch15-m0nd-f87e972c7cc3"
       deviceAttributesToIgnore = listOf("deviceId", "androidId", "serialNumber")
       isConsoleLogEnabled = true
   }
   ```

   ```swift
   await Protect.config { protectConfig in
       protectConfig.isBehavioralDataCollection = true
       protectConfig.isLazyMetadata = true
       protectConfig.envId = "3072206d-c6ce-ch15-m0nd-f87e972c7cc3"
       protectConfig.deviceAttributesToIgnore = ["deviceId", "serialNumber"]
       protectConfig.isConsoleLogEnabled = true
   }
   ```

   ```javascript
   const protectApi = protect({
       behavioralDataCollection: true,
       envId: "3072206d-c6ce-ch15-m0nd-f87e972c7cc3",
       deviceAttributesToIgnore: ['deviceId', 'serialNumber'],
   });
   ```

2. Call an initialize function to start the data collection using the configuration object:

   * Android

   * iOS

   * JavaScript

   ```kotlin
   Protect.init()

   Log.d("Protect", "Protect data collection initialized.")
   ```

   ```swift
   try await Protect.initialize()

   print("Protect data collection initialized.")
   ```

   ```javascript
   await protectApi.start();

   console.log('Protect data collection initialized.');
   ```

### Use the ProtectLifecycle module for Android or iOS

The DaVinci Client for Android and iOS provide the `ProtectLifecycle` module for simplifying the management of data collection.

As a DaVinci Client module, it is aware of the current state of authentication, and can automatically pause and resume behavioral data collection when required.

Configure the `ProtectLifecycle` module in your DaVinci Client configuration, as with other modules.

The available properties are as follows:

| **Parameter**                  | **Description**                                                                                                                            |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `envID`                        | Required. Your PingOne environment identifier.For example, `3072206d-c6ce-ch15-m0nd-f87e972c7cc3`                                          |
| `deviceAttributesToIgnore`     | Optional. A list of device attributes to ignore when collecting device signals.For example, `AUDIO_OUTPUT_DEVICES` or `IS_ACCEPT_COOKIES`. |
| `isBehavioralDataCollection`   | When `true`, collect behavioral data.Default is `true`.                                                                                    |
| `isConsoleLogEnabled`          | When `true`, output SDK log messages in the developer console.Default is `false`.                                                          |
| `isLazyMetadata`               | When `true`, calculate metadata on demand rather than automatically after calling `start`.Default is `false`.                              |
| `customHost`                   | Optional. Specify a custom host for the Protect API, which can be useful in specific deployment scenarios.                                 |
| `pauseBehavioralDataOnSuccess` | When `true`, the DaVinci Client automatically pauses behavioral data collection after successful authentication.Default is `false`.        |
| `resumeBehavioralDataOnStart`  | When `true`, the DaVinci Client automatically starts behavioral data collection when it initializes.                                       |

Example code:

* Android

* iOS

```kotlin
DaVinci {
    timeout = 30
    module(Oidc) {
        clientId = "dummy"
        // ... Other OIDC configuration
    }
    module(ProtectLifecycle) {
        isBehavioralDataCollection = true
        isLazyMetadata = true
        envId = "3072206d-c6ce-ch15-m0nd-f87e972c7cc3"
        deviceAttributesToIgnore = listOf("deviceId")
        isConsoleLogEnabled = true

        pauseBehavioralDataOnSuccess = true
        resumeBehavioralDataOnStart = true
    }
}
```

```swift
let davinci = DaVinci.createDaVinci { config in
    config.module(OidcModule.config) { oidcValue in
        oidcValue.clientId = "dummy"
        // ... Other OIDC configuration
    }
    config.module(ProtectLifecycleModule.config) { protectValue in
        protectValue.isBehavioralDataCollection = true
        protectValue.isLazyMetadata = true
        protectValue.envId = "3072206d-c6ce-ch15-m0nd-f87e972c7cc3"
        protectValue.deviceAttributesToIgnore = ["deviceId"]
        protectValue.isConsoleLogEnabled = true

        protectValue.pauseBehavioralDataOnSuccess = true
        protectValue.resumeBehavioralDataOnStart = true
    }
}
```

### On-demand initialization on receipt of ProtectCollector

You can choose not to initialize data collection on app startup and instead initialize it on-demand, when your DaVinci flow reaches the relevant node.

The DaVinci for Android and iOS will automatically initialize data collection when calling the `collect()` method in response to receiving a `ProtectCollector` from PingOne. Learn more in [Return captured data to a DaVinci flow](#return-data-davinci-flow).

In the DaVinci Client for JavaScript, you can use the `start()` method to initialize data collection in response to receiving a `ProtectCollector` from PingOne:

JavaScript example of on-demand initialization

```javascript
const collectors = davinciClient.getCollectors();
collectors.forEach((collector) => {
  if (collector.type === 'ProtectCollector') {
    // Optionally use configuration options from the flow to initialize the protect module
    const config = collector.output.config;

    // Initialize the Protect module and begin collecting data
    const protectApi = protect({
      envId: '3072206d-c6ce-ch15-m0nd-f87e972c7cc3',
      behavioralDataCollection: config.behavioralDataCollection,
      universalDeviceIdentification: config.universalDeviceIdentification,
    });
    await protectApi.start();
  }
});
```

|   |                                                                                                                                       |
| - | ------------------------------------------------------------------------------------------------------------------------------------- |
|   | Use the `collector.output.config` object to obtain the properties for initializing data collection as configured by the DaVinci flow. |

## Pause and resume behavioral data capture

Part of the data collection includes collecting behavioral data, such as how the user interacts with the app, to help when performing evaluations.

There are scenarios where you might want to manually pause the collection of behavioral data.

For example, the user might not be interacting with the app, or you only want to use device attribute data to be considered when performing PingOne Protect evaluations. You can then resume behavioral data collection when required.

The SDKs provide the `pauseBehavioralData()` and `resumeBehavioralData()` methods for manually pausing and resuming the capture of behavioral data:

* Android

* iOS

* JavaScript

```kotlin
Protect.pauseBehavioralData() // Pause data collection.
Protect.resumeBehavioralData() // Resume data collection.
```

```swift
await Protect.pauseBehavioralData() // Pause data collection.
await Protect.resumeBehavioralData() // Resume data collection.
```

```swift
await protectApi.pauseBehavioralData() // Pause data collection.
await protectApi.resumeBehavioralData() // Resume data collection.
```

## Return captured data to a DaVinci flow

To perform risk evaluations, the PingOne server requires the captured data.

On receipt of `ProtectCollector` your app should gather the collected data, update the collector, and call `next()` to return the collected data to PingOne:

* Android

* iOS

* JavaScript

On receipt of `ProtectCollector`, use the `collect()` method to populate the response with the captured data.

When the data is successfully collected, call `node.next()` to submit the data back to the server for evaluation.

```kotlin
node.collectors.forEach {
    when (it) {
        is ProtectCollector -> {
            when (val result = it.collect()) {
                is Success -> {
                    // Data collection successful: Proceed to the next node in the DaVinci flow.
                    node.next()
                }
                is Failure -> {
                    // Data collection failed: Implement robust error handling.
                    // Example: Log the error, display an informative message, or implement a retry mechanism.
                }
            }
        }
        // ... Handle other collector types
    }
}
```

On receipt of a `ProtectCollector` callback, use the `collect()` method to populate the response with the captured data.

When the data is successfully collected, call `node.next()` to submit the data back to the server for evaluation.

```swift
node.collectors.forEach { collector in
    switch collector {
    case let protectCollector as ProtectCollector:
        let result = await protectCollector.collect()
        switch result {
        case .success:
            // Data collection successful: Proceed to the next node in the DaVinci flow.
            node.next()
        case .failure:
            // Data collection failed: Implement robust error handling.
            // Example: Log the error, display an informative message, or implement a retry mechanism.
            break
        }
    // ... Handle other collector types
    default:
        break
    }
}
```

On receipt of a `ProtectCollector` callback, use the `getData()` method to prepare the collected data, and call the `update()` method populate the response with that captured data.

When the data is successfully collected, call `next()` to submit the data back to the server for evaluation.

```swift
async function onSubmitHandler() {
  try {
    const protectCollector = collectors.find((collector) => collector.type === 'ProtectCollector');

    // Update the Protect collector with the data collected
    if (protectCollector) {
      const updater = davinciClient.update(protectCollector);
      const data = await protectApi.getData();
      updater(data);
    }

    // Submit all collectors and get the next node in the flow
    await davinciClient.next();
  } catch (err) {
    // handle error
  }
}
```
