> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mangopay.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Profiler - iOS

> How to integrate the fraud prevention profiler on iOS.

<a href="/guides/fraud-prevention">Learn about fraud prevention</a>

<Note>
  **Note - Size overhead**

  The size of the framework you need to download is about 12 MB. It contains compiled versions for iOS Simulators and iOS Devices.

  For end users, the application download size will increase by 0.9 MB and the installed app size will increase by around 2.4 MB.
</Note>

<Info>
  **Prerequisites**

  To use the fraud prevention SDK (iOS), you’ll need:

  * iOS 11.0+
  * Xcode 12+
  * Merchant Number provided by Mangopay
</Info>

## Setting up your Artifactory account

Once you sign the fraud prevention contract with Mangopay, your assigned solutions engineer assesses the integration of your application.

If mobile integration is needed, you will be asked to provide the email addresses of your mobile developers. Once provided, JFrog accounts will be generated for your developers and they will receive an email to set up their account.

<Warning>
  **Caution - Locked accounts**

  In case of multiple failed login attempts, the system will temporarily suspend that user's account for a brief period of time during which additional login attempts will be ignored. To unlock your account, contact your assigned solutions engineer.
</Warning>

## Getting started

<Note>
  **Note - Sample project**

  There is a sample project available in Artifactory under the name [nethonesdk-ios-mangopay](https://nethone.jfrog.io/ui/repos/tree/General/nethonesdk-ios-mangopay), which may help integrate our SDK.
</Note>

### Installation using Swift Package Manager

<Info>
  **Prerequisites**

  This integration method requires:

  * Swift 5.7+
  * Xcode 15+
</Info>

**1. Create the Dependencies directory inside your project.**

```shell theme={null}
mkdir Dependencies
```

**2. Set it as your working directory.**

```shell theme={null}
cd Dependencies
```

**3. Initialize Swift package.**

```shell theme={null}
swift package init
```

**4. Remove unnecessary test files.**

```shell theme={null}
rm -r Tests
```

**5. Set up the repository and add your credentials to your .netrc file.**

1. Go to the [Nethone Artifactory Repository](https://nethone.jfrog.io/ui/repos/tree).
2. Find `nethonesdk-ios-mangopay-spm` repository.
3. In the top right corner, click the Set Me Up button

**6. Set Artifactory as a Swift repository.**

```shell theme={null}
swift package-registry set "https://nethone.jfrog.io/artifactory/api/swift/nethonesdk-ios-mangopay-spm"
```

**7. Add NethoneSDK dependency in the Package.swift file.**

```swift Swift theme={null}
// swift-tools-version: 5.7

import PackageDescription

let package = Package(
    name: "Dependencies",
    products: [
        .library(
            name: "Dependencies",
            targets: ["Dependencies"]),
    ],
    dependencies: [
        .package(id: "Nethone.NethoneSDK", exact: "2.17.1")
    ],
    targets: [
        .target(
            name: "Dependencies",
            dependencies: [
                .product(name: "NethoneSDK", package: "Nethone.NethoneSDK")
            ]),
    ]
)
```

**8. Resolve the package.**

```shell theme={null}
swift package resolve
```

**9. Add the Dependencies package to your Xcode project**

1. Open your Xcode project.
2. Navigate to *File* > *Add Package Dependencies*.
3. Click the *Add Local* button.
4. Select the *Dependencies* directory.

### Installation using CocoaPods (optional)

<Note>
  **Note - Contact Mangopay**

  If you want to use the <a href="https://cocoapods.org">CocoaPods</a> option, contact your assigned solutions engineer to create a CocoaPods repository for you.
</Note>

<Warning>
  **Caution - CocoaPods deprecation**

  CocoaPods will be deprecated in later updates. We recommend you use the Swift Package Manager installation.
</Warning>

**1. Install the latest version of CocoaPods**

```shell theme={null}
sudo gem install cocoapods
```

**2. Install Artifactory CocoaPods plugin**

```shell theme={null}
gem install cocoapods-art
```

**3. Add the Artifactory repository to your application directory**

```shell theme={null}
pod repo-art add nethonesdk-ios-mangopay-cocoapods "https://nethone.jfrog.io/nethone/api/pods/nethonesdk-ios-mangopay-cocoapods"
```

**4. Go to your application home directory and add the following code to your Podfile.**

```ruby theme={null}
platform :ios, '12.0'
use_frameworks!

plugin 'cocoapods-art', :sources => [
    'nethonesdk-ios-mangopay-cocoapods', 'trunk'
]

target '<Your Target Name>' do
    pod 'NethoneSDK'
end
```

**5. Install the pods files.**

```shell theme={null}
pod install
```

**Version update**

To update, you would first need to update the artifactory repository then the pods.

**1. Update the artifactory repository.**

```shell theme={null}
pod repo-art update nethonesdk-ios-mangopay-cocoapods
```

**2. Update the pods.**

```shell theme={null}
pod update
```

### Manually

1. Download the latest version of NethoneSDK.xcframework from your [Artifactory Repository](https://nethone.jfrog.io/ui/repos/tree). Find the `nethonesdk-ios-mangopay` repository and save it in the project folder.

2. Embed frameworks into your app target.
   In Xcode, go to the application target **General** settings. On the bottom, you can find ***Embedded Binaries*** section. Add NethoneSDK.xcframework by clicking the plus ***+*** button and choose the framework from your disk.

#### How to update

To update, download the new version of the NethoneSDK.xcframework from Artifactory and save it in the place as the previous version, thereby overwriting it.

## Initializing the SDK

Before you can use the framework, you must set the Merchant Number which is the identification number provided to you during the onboarding process.

To do so, you need to call `setMerchantNumber(...)` shortly after application starts.

For example in `didFinishLaunchingWithOptions`:

<CodeGroup>
  ```swift Swift theme={null}
  import NethoneSDK

  @UIApplicationMain
  class AppDelegate: UIResponder, UIApplicationDelegate {
      func application(_ application: UIApplication,
                       didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
          NTHNethone.setMerchantNumber("<your merchant number>");
          return true
      }
  }
  ```

  ```c Objective-C theme={null}
  #import <NethoneSDK/NethoneSDK.h>

  @implementation AppDelegate
  - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
      [NTHNethone setMerchantNumber:@"<your merchant number>"];
      return YES;
  }
  ```
</CodeGroup>

## Profiling attempt

### Attempt reference

The `attemptReference` is a unique, single-use identifier used to identify a specific profiling attempt. Your server will use this reference to query Nethone about the status of the attempt.

`AttemptReference` is generated by calling the `beginAttempt` function and is accessible by a static function named `attemptReference`.

<CodeGroup>
  ```swift Swift theme={null}
  let currentAttempt: String? = NTHNethone.attemptReference()
  ```

  ```c Objective-C theme={null}
  NSString *currentAttempt = [NTHNethone attemptReference];
  ```
</CodeGroup>

### Starting an attempt

<Note>
  **Note - Concurrency of profiling attempts**

  Only one profiling attempt can be running at a time. You should wait for this attempt to finish before starting the next one.
</Note>

To start an attempt, use the `NTHNethone.beginAttempt` method.

The behavioral data collection can also be turned off by setting the configuration variable `config.withoutBehavioralData `to `true` (swift) or `YES` (objective-c), and passing this configuration when starting the attempt.

<CodeGroup>
  ```swift Swift theme={null}
  override func viewWillAppear(_ animated: Bool) {
      //...
      let config = NTHAttemptConfiguration()
      config.sensitiveFields = ["input"]
      config.withoutBehavioralData = false
      try? NTHNethone.beginAttempt(with: config)
  }
  ```

  ```c Objective-C theme={null}
  - (void)viewWillAppear:(BOOL)animated {
      //...
          NTHAttemptConfiguration *config = [NTHAttemptConfiguration new];
          config.sensitiveFields = @[@"input"];
          config.withoutBehavioralData = NO;
      [NTHNethone  beginAttemptWithConfiguration:configerror:nil];
  }
  ```
</CodeGroup>

### Finalizing an attempt

<Warning>
  **Caution - Attempt and payment**

  You have to finalize the attempt before you process the payment.
</Warning>

The `finalizeButtonTapped` function makes sure that the necessary data has reached us. It is not recommended to terminate profiling and continue the flow until the function returns to the completion block.

<CodeGroup>
  ```swift Swift theme={null}
  func finalizeButtonTapped() {
      try? NTHNethone.finalizeAttempt(completion: { error in
          guard error == nil else {
              // Handle finalization error.
              // For example: internet is down
              return
          }
          // All data has been delivered to Nethone. Do the actual payment processing
      })
  }
  ```

  ```c Objective-C theme={null}
  - (void)finalizeButtonTapped {
      [NTHNethone finalizeAttemptWithCompletion:^(NSError * _Nullable completionError) {
          if (completionError) {
              // Handle finalization error.
              // For example: internet is down
              return;
          }
          // All data has been delivered to Nethone. Do the actual payment processing
      } error:nil];
  }
  ```
</CodeGroup>

### Sending custom attempt data

For custom data, you can use the `sendCustomAttemptData` method and add the `options` argument to specify the type of the custom data.

The available option is `DataTypeJson`.

<Warning>
  **Caution - Usage errors**

  Calling `sendCustomAttemptData` on a not started or already completed attempt, or passing a data type that does not match the one provided in the `options` argument will return an error.
</Warning>

<CodeGroup>
  ```swift Swift theme={null}
  let customData:[String: Any] = ["some_custom_data": 48151]
  try? NTHNethone.sendCustomAttemptData(customData, options: .DataTypeJson)
  ```

  ```c Objective-C theme={null}
  NSDictionary<NSString *, id> *customData = @{
      @"some_custom_data": @48151
  };
  [NTHNethone sendCustomAttemptData:customData options:DataTypeJson error:nil];
  ```
</CodeGroup>

### Canceling an attempt

<Warning>
  **Caution - Attempt reference usage**

  Once an attempt is canceled, the same attempt reference can not be reused.
</Warning>

Canceling an attempt immediately stops collecting and sending data without further checking whether the relevant data has been received by us.

<CodeGroup>
  ```swift Swift theme={null}
  override func viewDidDisappear(_ animated: Bool) {
      //...
      try? NTHNethone.cancelAttempt()
  }
  ```

  ```c Objective-C theme={null}
  - (void)viewDidDisappear:(BOOL)animated {
      //...
      [NTHNethone cancelAttemptWithError:nil];
  }
  ```
</CodeGroup>

## Errors

<table>
  <thead>
    <tr>
      <th class="header">Error code</th>
      <th class="header">Error message</th>
      <th class="header">Short description</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td class="table-content">kNTHApiErrorMerchantNumberNotSet = -1</td>
      <td class="table-content">Begin calls while the merchant number is not set.</td>
      <td class="table-content">Without setting the merchant number, profiling cannot be started and no data will be collected.</td>
    </tr>

    <tr>
      <td class="table-content">kNTHApiErrorAnotherAttemptOngoing = -2</td>
      <td class="table-content"> Begin called when another attempt is ongoing.</td>
      <td class="table-content">It is possible that the previous profiling has not yet completed finalization (after calling the `+finalizeAttemptWithCompletion:error:`). In this case, you should wait for it to finish before starting the next profiling. To force the immediate termination of the profiling, you can call `+cancelAttemptWithError:`. However, this may result in the collection of insufficient data.</td>
    </tr>

    <tr>
      <td class="table-content">kNTHApiErrorFinalizingAlreadyEndedAttempt = -5</td>
      <td class="table-content">Finalize or send custom data called when the attempt is already completed or canceled.</td>
      <td class="table-content">The last profiling has already been completed. Currently, no profiling is in progress and it is not possible to finalize it or send additional data as part of it. The `AttemptReference` of the last profiling can be found in error.userInfo under the key `kNTHNethoneAttemptReference`.</td>
    </tr>

    <tr>
      <td class="table-content">kNTHApiErrorCancelingAlreadyEndedAttempt = -15</td>
      <td class="table-content">Cancel called when the attempt is already completed or canceled.</td>
      <td class="table-content">The last profiling has already been completed. Currently, no profiling is in progress and it is not possible to cancel it.</td>
    </tr>

    <tr>
      <td class="table-content">kNTHApiErrorAttemptDuringFinalization = -13</td>
      <td class="table-content">Finalize or send custom data called when profiling has already been finalized.</td>
      <td class="table-content">Custom data cannot be sent during finalization or after profiling is completed.</td>
    </tr>

    <tr>
      <td class="table-content">kNTHApiErrorAttemptNotInitiated = -6</td>
      <td class="table-content">Finalize, cancel or send custom data called without begin called before.</td>
      <td class="table-content">Currently, no profiling has been started. It is not possible to finalize, cancel, or send additional data.</td>
    </tr>

    <tr>
      <td class="table-content">kNTHApiErrorInvalidCustomData = -14</td>
      <td class="table-content">Send custom data called with structure that cannot be parsed properly.</td>

      <td class="table-content" />
    </tr>
  </tbody>
</table>

## Optional features

<Note>
  **Note - Adding features**

  To use any of the optional features available, please contact your assigned solutions engineer.
</Note>

### Text field registration

**Register a text field**

The `registerTextField()` method will register a text field to send data from it.

The function takes a `mode` parameter which indicates the mode in which `textField` will collect data. The `NTHRegisterMode` enum provides constants for the data collecting options for a `textField` when registered.

There are 3 possible options:

* NTHRegisterMode.NoneData – No behavioral or text data will be collected.
* NTHRegisterMode.ContentFree –Only behavioral data will be collected.
* NTHRegisterMode.AllData – All behavioral and text data will be collected.

The function also takes a `name` parameter which describes the given `textField` in collected data. When left as `Null`, the value will be determined automatically, however it might be inaccurate.

The text fields are permanently registered, which means that data from these fields will be collected during each profiling. There is no need to register fields every time during the start of an attempt.

<CodeGroup>
  ```swift Swift theme={null}
  @IBOutlet weak var aTextFieldToRegister: UITextField!

  override func viewDidLoad() {
      //...
      NTHNethone.register(aTextFieldToRegister,
                          mode: .ContentFree,
                          name: "UserName")
  }
  ```

  ```c Objective-C theme={null}
  @property (nonatomic, weak) IBOutlet UITextField *aTextFieldToRegister;

  - (void)viewDidLoad {
      //...
      [NTHNethone registerTextField:aTextFieldToRegister
                               mode:NTHRegisterMode.NoneData
                               name:nil];
  }
  ```
</CodeGroup>

**Customizing data collection**

To collect data only from previously registered text fields instead of all UITextFields, you need to use the registered `textFields`.

<CodeGroup>
  ```swift Swift theme={null}
  registeredTextFieldsOnly = false
  ```

  ```c Objective-c theme={null}
  registeredTextFieldsOnly = NO
  ```
</CodeGroup>

### IDFA permissions

The identifier for advertisers (IDFA) is a unique identifier assigned by Apple to a user's iOS device. Advertisers are required to request permission to access the IDFA and track the user.

To use this feature, you will need to use the App Tracking Transparency (ATT) framework. You must also include a purpose string in the system prompt that explains why you'd like to track the user.

To access the values of the IDFA, you need:

* The user’s device on iOS 14.5+ .
* The user’s permission granted through the App Tracking Transparency prompt.

### Location permissions

According to [Apple’s Core Location](https://developer.apple.com/documentation/corelocation?language=objc) documentation, to enable data location collection during profiling, the application should handle authorization from the user before profiling starts.

To collect the relevant data, the application needs consent for “When In Use” with the Full Accuracy option selected.

```swift Swift theme={null}
//
//  NTHLocationManager.swift
//
//  Copyright © 2021 Nethone. All rights reserved.
//

/**
    If the user has denied access to the location.
    The SDK will not show any other popup and fail to get the location data silently.

    Before you implement a permission handling module,
    you need to add some explanation to your's app plist.

    <key>NSLocationWhenInUseUsageDescription</key>
    <string>We need this to ...</string>
    <key>NSLocationTemporaryUsageDescriptionDictionary</key>
    <dict>
        <key>ExampleUsageDescription</key>
        <string>We need this to ...</string>
    </dict>

    If your app already uses those, you don't need to change anything.
    <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
    <key>NSLocationAlwaysUsageDescription</key>

    If you app uses this, then it is preferred to remove it for getting more accurate location.
    <key>NSLocationDefaultAccuracyReduced</key>
    <true/>
*/

import CoreLocation

class NTHLocationManager: NSObject {
    private static var sharedLocationManager: NTHLocationManager = {
        let nlm = NTHLocationManager()
        nlm.locationManager.delegate = nlm
        return nlm
    }()

    class func shared() -> NTHLocationManager {
        return sharedLocationManager
    }

    private let locationManager: CLLocationManager

    private override init() {
        locationManager = CLLocationManager()
    }

    func handlePermissions() {
        if !CLLocationManager.locationServicesEnabled() {

            //TODO: Explain to the user to turn on location services in the settings
            //Otherwise, it will not be possible to collect location data

            return
        }
        checkStatus()
    }

    private func checkStatus() {
        var status: CLAuthorizationStatus?
        if #available(iOS 14.0, *) {
            status = locationManager.authorizationStatus
        } else {
            status = CLLocationManager.authorizationStatus()
        }

        switch status {
        case .notDetermined:
            locationManager.requestWhenInUseAuthorization()
        case .denied:

            //TODO: Convince the user to allow access to the location data
            //Otherwise, it will not be possible to collect location data

            break
        case .restricted:
            break
        case .authorizedAlways:
            if #available(iOS 14.0, *) {
                checkAccuracy()
            }
        case .authorizedWhenInUse:
            if #available(iOS 14.0, *) {
                checkAccuracy()
            }
        default:
            break
        }
    }

    @available(iOS 14.0, *)
    private func checkAccuracy() {
        switch locationManager.accuracyAuthorization {
        case .fullAccuracy:
            break
        case .reducedAccuracy:
            requestFullAccuracy()
        default:
            break
        }
    }

    @available(iOS 14.0, *)
    func requestFullAccuracy() {
        locationManager
            .requestTemporaryFullAccuracyAuthorization(withPurposeKey: "ExampleUsageDescription")
            { [weak self] (error) in
                guard error == nil else { return }

                switch self?.locationManager.accuracyAuthorization {
                case .fullAccuracy:
                    break
                case .reducedAccuracy:

                    //TODO: Convince the user to allow access to a precise location data and request again
                    //Otherwise, it will not be possible to collect precise location data

                    break
                default:
                    break
                }
            }
    }
}

extension NTHLocationManager: CLLocationManagerDelegate {
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        checkStatus()
    }

    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { }
}
```

### Active call detection

<Note>
  **Note - Applicable to Chinese market**

  If the app is also being distributed in China, this feature is required during the Apple Review process.
</Note>

The use of CallKit is prohibited in China. Due to this, Apple has introduced an additional check to make sure that the regulation is fulfilled.  We made sure our solution does not use CallKit in China by using the `NSLocale.regionCode` function.

Apple detects the use of CallKit during the Apple Review process and makes sure the developer is aware of the regulation by rejecting the application with a message. You need to reply that your CallKit implementation is not active in China, and include a short message in the Reviewer Information section saying: "In this version and next we do not use CallKit for users in China. We detect the user's region with `NSLocale`".

## Related resources

<CardGroup col={2}>
  <Card title="Guide" href="/guides/fraud-prevention">Learn more about fraud prevention</Card>
</CardGroup>
