> ## 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.

# Remote UI

> Отображайте плейсменты PNLight Remote UI, например пейволы и экраны онбординга, в приложении для iOS, React Native или Flutter и обрабатывайте действия пользователей.

PNLight Remote UI позволяет приложению отображать экраны, настроенные в дашборде. Используйте их для пейволов, подсказок, экранов обновления подписки и других элементов приложения, которые нужно менять без выпуска новой версии.

## Выбор Remote UI

Приложение запрашивает плейсмент с локалью и идентификатором пользователя. PNLight возвращает конфигурацию из соответствующего плейсмента дашборда, если он включён и подходящая конфигурация доступна.

Создайте в разделе **Remote UI** каждый плейсмент, который может запросить приложение. При новом запросе к PNLight, например с параметром `ignoreCache`, UI не возвращается, если плейсмент отсутствует, отключён, удалён или для него нет подходящей конфигурации локали.

Дашборд отслеживает:

* Запросы: пользователи, запросившие плейсмент.
* Показы: пользователи, получившие конфигурацию UI, которую можно было показать.

До возврата конфигурации PNLight также может применить таргетинг, правила однократного показа, защиту от записи экрана и подстановку значений во время выполнения. Элементы управления описаны в разделе [Плейсменты Remote UI](/ru/dashboard/remote-ui-placements).

Заполнители времени выполнения обрабатываются на сервере. SDK получает конечные значения конфигурации, например идентификаторы продуктов, после того как PNLight заменит токены вроде `{{monthlyProductId}}`.

## Отображение плейсмента

`RemoteUiView` вызывает `getUIConfig` внутри. При первом запуске приложения с атрибуцией AppsFlyer подождите 4–5 секунд после инициализации AppsFlyer перед показом плейсмента, чтобы данные конверсии успели поступить. Если `attributionRequired` равно `true`, SDK также ожидает атрибуцию внутри перед запросом конфигурации.

<CodeGroup>
  ```swift SwiftUI theme={null}
  RemoteUiView(placement: "paywall", cardId: "paywall_card") { action in
      if action.logId == "purchase_button" {
          let productId = action.params["id"] ?? ""
          Task {
              let success = await purchase(productId)
              if success {
                  showMainScreen()
              }
          }
      }
  }
  ```

  ```tsx React Native theme={null}
  <RemoteUiView
    placement="paywall"
    cardId="paywall_card"
    style={{ flex: 1 }}
    onAction={(event) => {
      if (event.logId === "purchase_button") {
        startPurchase(event.params.id).then(() => {
          navigation.navigate("Main");
        });
      }
    }}
  />
  ```

  ```dart Flutter theme={null}
  RemoteUiView(
    placement: 'paywall',
    cardId: 'paywall_card',
    onAction: (event) async {
      if (event.path == 'purchase') {
        final success = await startPurchase(event.params['id']);
        if (success) {
          openMainScreen();
        }
      }
    },
  )
  ```
</CodeGroup>

## Компоненты Remote UI

В разделе [Компоненты Remote UI](/ru/sdk/remote-ui-components) описано, как добавлять компоненты PNLight в конфигурацию и настраивать их свойства.

## Обработка действий

Действия Remote UI передаются приложению. Используйте идентификаторы действий, пути или параметры запроса, чтобы определить поведение приложения. Разные плейсменты могут возвращать разные действия, поэтому обрабатывайте их явно.

Распространённые действия:

* Начать покупку.
* Вернуть пользователя на главный экран приложения после успешной подписки.
* Закрыть экран.
* Открыть другой экран приложения.
* Отследить взаимодействие с продуктом.

<Warning>
  Всегда обрабатывайте неизвестные действия. Безопасный резервный вариант — закрыть экран Remote UI или проигнорировать действие.
</Warning>

## Получение конфигураций вручную

Используйте `getUIConfig`, если нужно самостоятельно получить конфигурацию плейсмента. При первом запуске приложения, когда требуется атрибуция, вызывайте его после отправки атрибуции и подождите 4–5 секунд после инициализации AppsFlyer перед запросом. SDK всё равно ожидает атрибуцию внутри, если `attributionRequired` равно `true`.

<CodeGroup>
  ```swift Swift theme={null}
  try? await Task.sleep(nanoseconds: 5_000_000_000)

  let config = await PNLightSDK.shared.getUIConfig(placement: "paywall")
  let freshConfig = await PNLightSDK.shared.getUIConfig(
      placement: "paywall",
      ignoreCache: true
  )
  ```

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

  await new Promise((resolve) => setTimeout(resolve, 5000));

  const config = await getUIConfig("paywall");
  const freshConfig = await getUIConfig("paywall", true, true);
  ```

  ```dart Flutter theme={null}
  await Future<void>.delayed(const Duration(seconds: 5));

  final config = await PNLightSDK.getUIConfig('paywall');
  final freshConfig = await PNLightSDK.getUIConfig(
    'paywall',
    ignoreCache: true,
  );
  ```
</CodeGroup>

## Работа кеша конфигураций

По умолчанию конфигурации Remote UI сначала загружаются из кеша. Если кешированная конфигурация существует, SDK может сразу вернуть её и обновить в фоновом режиме с помощью запроса ETag. Благодаря этому повторные показы остаются быстрыми, а изменения из дашборда продолжают поступать.

Используйте `ignoreCache`, когда приложение должно дождаться последнего ответа сервера, например во время QA или после изменения состояния пользователя, которое должно повлиять на таргетинг.

## Обработка отсутствующих конфигураций и ошибок

При новом запросе к PNLight значение `null` или пустая конфигурация означает, что PNLight не вернул UI для плейсмента. Это может произойти, если плейсмента нет в разделе **Remote UI**, он отключён, условия таргетинга не совпали, правило однократного показа запретило показ, защита от записи экрана заблокировала пользователя, нет конфигурации для подходящей локали или не удалось обработать заполнитель времени выполнения.

Оболочки React Native и Flutter сообщают об ошибках загрузки отдельно от отсутствующего UI. Используйте `onError` в `RemoteUiView` или обработайте ошибки `getUIConfig` при ручном запросе.

Приложения Swift при ручном запросе могут использовать `getUIConfigResult`, чтобы различать все результаты:

```swift theme={null}
let result = await PNLightSDK.shared.getUIConfigResult(placement: "paywall")

switch result {
case .success(let config?):
    render(config)
case .success(nil):
    showLocalFallback()
case .failure:
    showErrorFallback()
}
```

## Настройка в дашборде

Создавайте плейсменты и управляйте ими в разделе **Remote UI**. Процесс работы в дашборде описан в разделе [Плейсменты Remote UI](/ru/dashboard/remote-ui-placements).
