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

# Атрибуция

> Передавайте данные атрибуции из AppsFlyer или другого поставщика мобильной атрибуции в PNLight, чтобы правильно связывать установки, запросы Remote UI и кампании.

Атрибуция связывает пользователя с каналом или кампанией, которые привели его в приложение.

## Как атрибуция работает в приложении

Поддерживаемый поставщик атрибуции, например AppsFlyer, Firebase или Facebook, определяет данные атрибуции. Когда поставщик вернёт данные о конверсии, отправьте их в PNLight с помощью SDK.

PNLight использует настройки атрибуции проекта, чтобы определить приоритет источников, если одного пользователя описывают несколько источников.

## Рекомендуемый процесс для AppsFlyer

<Steps>
  <Step title="Запустите кампанию">
    Запустите кампанию в AppsFlyer и используйте ссылки AppsFlyer для привлечения пользователей.
  </Step>

  <Step title="Получите идентификатор AppsFlyer">
    Когда пользователь открывает ссылку кампании, AppsFlyer создаёт для него уникальный идентификатор.
  </Step>

  <Step title="Запросите разрешение ATT">
    После установки приложения запросите разрешение App Tracking Transparency, если приложению нужна атрибуция на основе IDFA.
  </Step>

  <Step title="Обновите IDFA в PNLight">
    Если PNLight инициализирован до ответа ATT, вызовите `updateIdfa()` после получения разрешения.
  </Step>

  <Step title="Инициализируйте AppsFlyer">
    Инициализируйте SDK AppsFlyer после ответа ATT.
  </Step>

  <Step title="Передайте идентификатор пользователя PNLight">
    При необходимости задайте идентификатор пользователя PNLight в инструментах атрибуции, например через `appsFlyer.setCustomerUserId`. Тогда во всех инструментах будет отображаться один идентификатор пользователя.
  </Step>

  <Step title="Отправьте данные о конверсии в PNLight">
    Получите результат конверсии AppsFlyer и отправьте его в PNLight с помощью `addAttribution`.
  </Step>

  <Step title="Запросите Remote UI">
    При первом запуске приложения подождите 4–5 секунд после инициализации AppsFlyer, прежде чем вызывать `getUIConfig` или показывать `RemoteUiView`. Если `attributionRequired` имеет значение `true`, SDK также ожидает атрибуцию внутри приложения.
  </Step>
</Steps>

## Отправьте данные атрибуции

<CodeGroup>
  ```swift Swift theme={null}
  await PNLightSDK.shared.addAttribution(
      provider: .appsFlyer,
      data: ["af_status": "Non-organic"],
      identifier: "appsFlyerCustomerId"
  )

  await PNLightSDK.shared.updateIdfa()
  ```

  ```tsx React Native theme={null}
  import { addAttribution, updateIdfa } from "@pnlight/sdk-react-native";

  await addAttribution(
    "appsFlyer",
    { af_status: "Non-organic" },
    "appsFlyerCustomerId"
  );

  await updateIdfa();
  ```
</CodeGroup>

## Примеры для AppsFlyer

<CodeGroup>
  ```swift Swift theme={null}
  import AppTrackingTransparency
  import AppsFlyerLib
  import PNLightSDK
  import UIKit

  final class AppDelegate: NSObject, UIApplicationDelegate, AppsFlyerLibDelegate {
      func application(
          _ application: UIApplication,
          didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
      ) -> Bool {
          AppsFlyerLib.shared().appsFlyerDevKey = "your-appsflyer-dev-key"
          AppsFlyerLib.shared().appleAppID = "your-ios-app-id"
          AppsFlyerLib.shared().delegate = self

          Task {
              await PNLightSDK.shared.initialize(apiKey: "pnlight_sdk_token")
              await ATTrackingManager.requestTrackingAuthorization()
              await PNLightSDK.shared.updateIdfa()
              AppsFlyerLib.shared().customerUserID = PNLightSDK.shared.getUserId()
              AppsFlyerLib.shared().start()
          }

          return true
      }

      func onConversionDataSuccess(_ installData: [AnyHashable: Any]) {
          let data = Dictionary(uniqueKeysWithValues: installData.compactMap { key, value in
              guard let key = key as? String else { return nil }
              return (key, value)
          })

          Task {
              await PNLightSDK.shared.addAttribution(
                  provider: .appsFlyer,
                  data: data,
                  identifier: AppsFlyerLib.shared().getAppsFlyerUID()
              )
          }
      }

      func onConversionDataFail(_ error: Error) {
          print("AppsFlyer conversion data error:", error)
      }
  }
  ```

  ```tsx React Native theme={null}
  import appsFlyer from "react-native-appsflyer";
  import { requestTrackingPermission } from "react-native-tracking-transparency";
  import {
    addAttribution,
    getUserId,
    initialize,
    updateIdfa,
  } from "@pnlight/sdk-react-native";

  const getAppsFlyerUID = () =>
    new Promise<string | null>((resolve) => {
      appsFlyer.getAppsFlyerUID((error, uid) => {
        if (error) {
          console.error("AppsFlyer UID error", error);
          resolve(null);
          return;
        }

        resolve(uid);
      });
    });

  appsFlyer.onInstallConversionData(async (conversionData) => {
    const appsFlyerId = await getAppsFlyerUID();

    await addAttribution(
      "appsFlyer",
      conversionData?.data ?? conversionData,
      appsFlyerId
    );
  });

  await initialize("pnlight_sdk_token");
  await requestTrackingPermission();
  await updateIdfa();

  appsFlyer.initSdk({
    devKey: "your-appsflyer-dev-key",
    appId: "your-ios-app-id",
    isDebug: false,
    onInstallConversionDataListener: true,
  }, async () => {
    const pnlightUserId = await getUserId();
    appsFlyer.setCustomerUserId(pnlightUserId);
  });
  ```

  ```dart Flutter theme={null}
  import 'package:app_tracking_transparency/app_tracking_transparency.dart';
  import 'package:appsflyer_sdk/appsflyer_sdk.dart';
  import 'package:pnlight_sdk/pnlight_sdk.dart';

  final appsFlyerOptions = AppsFlyerOptions(
    afDevKey: 'your-appsflyer-dev-key',
    appId: 'your-ios-app-id',
    showDebug: false,
  );

  final appsFlyerSdk = AppsflyerSdk(appsFlyerOptions);

  await PNLightSDK.initialize('pnlight_sdk_token');
  await AppTrackingTransparency.requestTrackingAuthorization();
  await PNLightSDK.updateIdfa();

  appsFlyerSdk.onInstallConversionData((conversionData) async {
    final appsFlyerId = await appsFlyerSdk.getAppsFlyerUID();
    final payload = conversionData['payload'] ?? conversionData;

    await PNLightSDK.addAttribution(
      provider: 'appsFlyer',
      data: Map<String, dynamic>.from(payload as Map),
      identifier: appsFlyerId,
    );
  });

  await appsFlyerSdk.initSdk(
    registerConversionDataCallback: true,
    registerOnAppOpenAttributionCallback: false,
    registerOnDeepLinkingCallback: false,
  );
  ```
</CodeGroup>

<Tip>
  Большая часть ожидания перед первым запросом Remote UI связана с атрибуцией AppsFlyer. Если атрибуция обязательна, подождите 4–5 секунд после инициализации AppsFlyer.
</Tip>

## Поддерживаемые имена поставщиков

| SDK          | Значения поставщика                       |
| ------------ | ----------------------------------------- |
| Swift        | `.appsFlyer`, `.firebase`, `.facebook`    |
| React Native | `"appsFlyer"`, `"firebase"`, `"facebook"` |
| Flutter      | `"appsFlyer"`, `"firebase"`, `"facebook"` |

## Настройки в панели управления

Откройте **Settings** и настройте приоритет атрибуции:

* Поместите предпочтительный источник на первое место.
* Оставьте выбранным хотя бы один источник.
* Задайте период ожидания предпочтительной атрибуции.

<Note>
  Более длительный период ожидания может повысить полноту атрибуции, но также способен задержать ранние решения, которые от неё зависят.
</Note>
