# Контекст экранов и события iOS

## PageContext

`PageContext` описывает, где находится пользователь в приложении и в каком бизнес-контексте выполняется запрос.

Этот объект используется в ключевых вызовах iOS SDK:

- [`trackView(...)`](#trackviewpagecontextviewcontroller) для передачи просмотров экранов;
- [`triggerEvent(...)`](#triggereventeventspagecontextviewcontroller) для передачи действий пользователя;
- [`getContentBySelector(...)`](./custom_ui.md#getcontentbyselectorselectorpagecontext) для ручной загрузки контента.

Корректное заполнение `PageContext` влияет и на аналитику, и на таргетинг, и на подбор контента.

### Поля PageContext

[Конструктор и типы полей](./api_reference.md#pagecontext).

| Поле | Обязательность | Описание |
| :--- | :--- | :--- |
| `type` | Обязательно | Тип экрана. |
| `data` | Обязательно | Контекстные данные для выбранного типа экрана. |
| `location` | Обязательно | Уникальный идентификатор экрана, маршрута или deeplink. |
| `lng` | Опционально | Региональный код для мультирегиональности. |
| `pageNumber` | Опционально | Номер страницы в пагинации. |
| `referrer` | Опционально | Источник перехода. |
| `utm` | Опционально | UTM-метки. |
| `attributes` | Опционально | Дополнительные атрибуты таргетинга. |

Бизнес-логика заполнения `PageContext` соответствует [Page context](/Integration/web_integration/page_context.md).

Рекомендуемая схема заполнения:

- `HOMEPAGE`: `data = []`.
- `PRODUCT`: в `data` передается SKU товара ровно как в товарном фиде, без нормализации и с тем же регистром.
- `CART`: в `data` передаются SKU всех товаров, которые сейчас находятся в корзине, ровно как в товарном фиде, без нормализации и с тем же регистром.
- `CATEGORY`: в `data` передается полная иерархия категорий от самой широкой до самой узкой ровно как в товарном фиде, без нормализации и с тем же регистром.
- `SEARCH`: в `data` передается поисковый запрос одной строкой. Для пустого поиска передавайте пустой список.
- `OTHER`: используйте только для экранов, которые не подходят под остальные типы.

Для любых значений в `PageContext`, которые должны матчиться с товарным фидом, действует единое правило: передавайте их идентично фиду. Нельзя менять написание, переименовывать значения или приводить их к upper/lower case.

### Поле `lng`

`lng` используется только для мультирегиональности. Это региональный код, который позволяет отдавать пользователю корректные региональные данные по товарам: цены, доступность и остатки.

Например, если пользователь находится в Новосибирске, значение `lng` можно использовать для того, чтобы в рекомендациях участвовали только товары, реально доступные в Новосибирске.

Важно:

- значения `lng` в `PageContext` и в товарном фиде должны совпадать полностью, включая регистр;
- `lng`, как и любые другие feed-derived значения, нужно передавать без преобразования к upper/lower case;
- передавайте `lng` только если проект действительно использует региональные варианты данных.

### Поле `attributes`

SDK автоматически дополняет `attributes` служебными значениями:

- `app_version`
- `sdk_version`
- `app_platform` (`ios`).

При совпадении ключей iOS SDK сохраняет значение из переданного `attributes`. Поэтому не переопределяйте служебные ключи без необходимости. Android SDK обрабатывает эти коллизии иначе.

Настройка кампаний по этим значениям описана в разделе [Таргетинг SDK-кампаний по платформе и версиям](/personalization/Campaigns/targeting_schedule.md#таргетинг-sdk-кампаний-по-платформе-и-версиям).

### Примечания по текущей версии iOS SDK

- `sdk_version` передается из текущей версии SDK; для релиза `1.0.6` это `"1.0.6"`;
- `referrer` в публичной модели `PageContext` корректно передается через инициализатор;
- `pageNumber`, `referrer`, `utm` и `attributes` сохраняются при сборке контекста для `/visit`, `/event` и `/choose`.

См. также:

- [`trackView(...)`](#trackviewpagecontextviewcontroller)
- [`triggerEvent(...)`](#triggereventeventspagecontextviewcontroller)
- [`getContentBySelector(...)`](./custom_ui.md#getcontentbyselectorselectorpagecontext)

## Отслеживание просмотров экранов

### `trackView(pageContext:viewController:)`

`trackView(...)` передает в Gravity Field факт просмотра экрана. Этот вызов используется и для трекинга пользовательского поведения, и для сценариев, в которых показ кампании зависит от контекста страницы.

Пример:

```swift
GravitySDK.instance.trackView(
    pageContext: PageContext(
        type: .product,
        data: ["sku-123"],
        location: "app://product/sku-123"
    )
)
```

Что важно учесть:

- для корректной бизнес-логики требуется корректно заполненный [`PageContext`](#pagecontext);
- если `viewController` не передан, SDK пытается найти top-most `UIViewController` автоматически;
- для автопоказа in-app нужен доступный `UIViewController`, уже находящийся в `window`;
- при успешной загрузке контента для найденных кампаний SDK автоматически отправляет content load tracking;
- метод запускает асинхронную работу внутри `Task` и не возвращает результат вызывающему коду напрямую.

## Трекинг событий

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

Бизнес-логика заполнения событий соответствует [Настройке передачи событий](/Integration/web_integration/events.md), а iOS SDK предоставляет типы и метод для их отправки.

Для e-commerce сценариев события `PurchaseEvent` и `AddToCartEvent` обязательны.

Для всех полей событий, которые должны матчиться с товарным фидом, действует то же правило, что и для `PageContext`: передавайте значения идентично фиду. Это относится к `productId`, `cart[*].productId` и другим feed-derived значениям. Регистр является частью значения.

### `triggerEvent(events:pageContext:viewController:)`

`triggerEvent(...)` передает в Gravity Field пользовательские действия. Этот вызов нужен и для аналитики, и для сегментации, и для сценариев, в которых событие может активировать кампанию.

Пример:

```swift
GravitySDK.instance.triggerEvent(
    events: [
        AddToCartEvent(
            value: 1990,
            productId: "sku-123",
            quantity: 1,
            currency: "RUB"
        )
    ],
    pageContext: PageContext(
        type: .product,
        data: ["sku-123"],
        location: "app://product/sku-123"
    )
)
```

Что важно учесть:

- как и для `trackView(...)`, для корректной бизнес-логики нужен корректно заполненный [`PageContext`](#pagecontext);
- для автопоказа in-app нужен доступный `UIViewController`, уже находящийся в `window`;
- при успешной загрузке контента для найденных кампаний SDK автоматически отправляет content load tracking;
- метод запускает асинхронную работу внутри `Task` и не возвращает результат вызывающему коду напрямую.

### Правила заполнения e-commerce событий

#### `PurchaseEvent`

- отправляйте после успешного завершения заказа;
- `uniqueTransactionId` должен быть уникальным для каждой покупки;
- `value` — полная сумма заказа;
- `cart` — фактический состав заказа;
- `cart[*].productId` должен совпадать со SKU в товарном фиде полностью, включая регистр;
- `currency` обязательна для мультивалютных проектов.

```swift
let purchase = PurchaseEvent(
    uniqueTransactionId: "ORDER-1001",
    value: 3580,
    cart: [
        CartItem(productId: "sku-123", quantity: 1, itemPrice: 1990),
        CartItem(productId: "sku-555", quantity: 1, itemPrice: 1590)
    ],
    currency: "RUB"
)
```

#### `AddToCartEvent`

- отправляйте в момент фактического добавления товара в корзину;
- `value` — сумма, добавляемая этим действием;
- `quantity` — количество единиц, добавленных именно этим действием;
- `productId` должен совпадать со SKU в товарном фиде полностью, включая регистр.

```swift
let addToCart = AddToCartEvent(
    value: 1990,
    productId: "sku-123",
    quantity: 1,
    currency: "RUB"
)
```

#### `RemoveFromCartEvent` и `SyncCartEvent`

- `RemoveFromCartEvent` отправляйте в момент удаления товара из корзины или уменьшения количества;
- `value` в `RemoveFromCartEvent` — сумма удаляемых единиц товара;
- `productId` в `RemoveFromCartEvent` должен совпадать со SKU в товарном фиде полностью, включая регистр;
- `SyncCartEvent` используйте, когда нужно передать полное актуальное состояние корзины;
- `value` в `SyncCartEvent` — общая стоимость актуального состава корзины.
- `cart[*].productId` в `RemoveFromCartEvent` и `SyncCartEvent`, если передается `cart`, должен совпадать со SKU в товарном фиде полностью, включая регистр.

#### Авторизация

Формирование CUID и пример LoginEvent — в [Идентификации пользователя](./identity.md).

#### `AddToWishlistEvent`

```swift
let addToWishlist = AddToWishlistEvent(
    value: 1990,
    productId: "sku-123"
)
```

`productId` в `AddToWishlistEvent` должен совпадать со SKU в товарном фиде полностью, включая регистр.

#### `CustomEvent`

```swift
let custom = CustomEvent(
    type: "survey-completed-v1",
    name: "Survey Completed",
    customProps: ["score": "9", "variant": "A"]
)
```

Параметры всех типов событий собраны в [справочнике API](./api_reference.md#события-triggerevent). При ошибке сети SDK не сохраняет событие в очередь: см. [Сеть и жизненный цикл](./network.md).
