# Баннеры

Баннеры — это встраиваемые рекламные блоки внутри контента приложения c каруселями или одиночными баннерами. В каждом креативе поддерживается отображение картинок и видео.

> [!NOTE]
> Размер баннера подстраивается под ширину экрана с сохранением соотношения сторон. Высота высчитывается динамически.

![Баннеры](../../../../../../concepts/multiformat-preview.png){height="200"}

Подробнее о форматах - в разделе [Рекламные форматы](../../../../../concepts/ad-formats.md).

## 1. Загрузка рекламы

При загрузке рекламы необходимо передать `AdSlot`, содержащий `padId` (идентификатор места размещения) и `position` (позиция в списке). `padId` можно получить в рекламной админке либо от менеджера. Для отладки можно использовать тестовые `padId` (см. [Отладка](../debugging.md)).

+++ Delegate

Создайте загрузчик и подпишитесь через делегат:

```swift
import MadSDK

private let loader = InlineAdLoader()
```

```swift
loader.delegate = self
```

Вызовите `loader.load(...)`:

```swift
let request = InlineAdRequest(
    slot: AdSlot(padId: "1"),
    targetings: ["some_targeting": "value"]
)

loader.load(request)
```

Отмена загрузки:

```swift
let cancellable = loader.load(request)

// Отмена в любой момент
cancellable.cancel()
```

+++ Async

Для загрузки вызовите `MadsSDK.inLine.load(...)`:

```swift
import MadSDK

let request = InlineAdRequest(
    slot: AdSlot(padId: "1"),
    targetings: ["some_targeting": "value"]
)

let response = await MadsSDK.inLine.load(request)
```

Простановка таймаута загрузки рекламы:

```swift
let task = Task {
    await MadsSDK.inLine.load(request)
}

Task {
    try? await Task.sleep(for: .seconds(10))
    task.cancel()
}
```

Отмена загрузки:

```swift
let task = Task {
    await MadsSDK.inLine.load(request)
}

// Отмена в любой момент
task.cancel()
```

+++

### Параметры запроса (InlineAdRequest)

| Параметр | Тип | Обязательный | Описание |
|----------|-----|:---:|----------|
| `slot` | `AdSlot` | ✅ | Идентификатор места размещения |
| `targetings` | `[String: String]` | ❌ | Словарь таргетингов для персонализации рекламы (по умолчанию: `[:]`) |

### Параметры AdSlot

при наличии нескольких рекламных блоков на экране можно передавать один padId и разные position.
Например, в листинге товаров InLine блок может повторяться через каждые 3 ряда карточек. Необходимо для первой позиции передавать `position: 0`, для второй `position: 1` и так далее, при этом padId один и тотже.

| Параметр | Тип | Обязательный | Описание |
|----------|-----|:---:|----------|
| `padId` | `String` | ✅ | Id места размещения |
| `position` | `Int` | ❌ | Позиция внутри места размещения (по умолчанию: `0`) |

> [!TIP]
> Параллельно можно загружать несколько рекламных объявлений. Для Delegate — создайте несколько экземпляров `InlineAdLoader`. Для Async — запустите загрузку в отдельных `Task`, для отмены вызовите `task.cancel()`. Для управления множеством слотов рекомендуется `InlineAdProvider` (см. раздел 6).

## 2. Таргетирование

Передайте словарь таргетингов при загрузке рекламы. Подробнее о таргетингах — в разделе [Таргетирование](../../../../../concepts/targeting.md).

+++ Delegate
```swift
let targetings: [String: String] = ["gender": "female"]

loader.load(
    InlineAdRequest(
        slot: AdSlot(padId: "1"),
        targetings: targetings
    )
)
```
+++ Async
```swift
let targetings: [String: String] = ["gender": "female"]

let request = InlineAdRequest(
    slot: AdSlot(padId: "1"),
    targetings: targetings
)

let response = await MadsSDK.inLine.load(request)
```
+++

## 3. Обработка результата запроса и встраивание рекламы

Обработайте результат загрузки и встройте рекламу в интерфейс приложения:

+++ Delegate
```swift
extension ViewController: InlineAdLoaderDelegate {
    func inlineAdLoader(
        _ loader: InlineAdLoaderProtocol,
        didReceive response: InlineAdLoader.Response
    ) {
        switch response {
        case let .success(inlineAd, slot):
            // Получить UIView для встраивания
            let adView = MadsSDK.render(inlineAd, inVC: self)
            container.addSubview(adView)
        case let .failure(error, slot):
            // Загрузка завершилась с ошибкой
            break
        case let .noContent(slot):
            // Реклама не была подобрана
            break
        }
    }
}
```
+++ Async
```swift
let response = await MadsSDK.inLine.load(request)

switch response {
case let .success(inlineAd, slot):
    // Получить UIView для встраивания
    let adView = MadsSDK.render(inlineAd, inVC: self)
    container.addSubview(adView)
case let .failure(error, slot):
    // Загрузка завершилась с ошибкой
    break
case let .noContent(slot):
    // Реклама не была подобрана
    break
}
```
+++

### Возможные состояния

| Тип значения | Параметры | Описание |
|--------------|-----------|----------|
| `success` | `inlineAd` — загруженная реклама, `slot` — информация о размещении | Реклама загружена успешно. Получить view вызвав `MadsSDK.render(...)`. |
| `failure` | `error` — причина ошибки, `slot` — информация о размещении | Загрузка рекламы завершилась ошибкой. |
| `noContent` | `slot` — информация о размещении | Запрос завершился без ошибок, но реклама не была подобрана. |

### Возможные ошибки загрузки (InlineAdLoadError)

| Тип значения | Описание |
|--------------|----------|
| `network` | Ошибка сети при загрузке рекламы |
| `sdkNotInitialized` | SDK не инициализирован |
| `adLoad` | Не удалось загрузить рекламу |
| `requestTimeout` | Превышен таймаут загрузки рекламы (см. [`adRequestTimeoutMillis`](../../quickstart.md)) |
| `cancelled` | Загрузка рекламы отменена |

> [!TIP]
> Параметр `viewController` в `MadsSDK.render(_:inVC:)` является опциональным. Если не указан — SDK определяет хост-контроллер автоматически через responder chain.

## 4. Реакция на действия пользователя

SDK не реагирует на действия пользователя на рекламном объявлении (такие как "нажатие на кнопку" и т.д.). Реакцию на эти действия необходимо реализовать на стороне интегрирующего приложения:

+++ Delegate

Подпишитесь на события рекламного объекта через делегат:

```swift
inlineAd.delegate = self
```

Реализуйте протокол:

```swift
extension ViewController: InlineAdDelegate {
    func inlineAd(_ ad: InlineAd, didEmit action: InlineAd.Action) {
        switch action {
        case let .onUrlClicked(info, type, url):
            openUrl(url) // открытие ссылки
        case let .onPromocodeCopy(info, promocode):
            applyPromocode(promocode) // применение промокода
        case let .onCustomAction(info, action, data):
            handleCustomAction(action, data: data) // обработка кастомного действия
        }
    }

    func inlineAd(_ ad: InlineAd, didEmit event: InlineAd.Event) {
        // см. секцию «5. Реакция на события показа рекламы»
    }
}
```

+++ Async
```swift
for await action in inlineAd.actions {
    switch action {
    case let .onUrlClicked(info, type, url):
        openUrl(url) // открытие ссылки
    case let .onPromocodeCopy(info, promocode):
        applyPromocode(promocode) // применение промокода
    case let .onCustomAction(info, action, data):
        handleCustomAction(action, data: data) // обработка кастомного действия
    }
}
```
+++

### Возможные значения InlineAd.Action

| Тип значения | Параметры | Описание |
|--------------|-----------|---------|
| `onUrlClicked` | `info` — информация о рекламном объявлении<br>`type` — тип ссылки<br>`url` — ссылка для перехода | Нажатие на кнопку перехода по ссылке |
| `onPromocodeCopy` | `info` — информация о рекламном объявлении<br>`promocode` — промокод для копирования | Нажатие на кнопку копирования промокода |
| `onCustomAction` | `info` — информация о рекламном объявлении<br>`action` — идентификатор действия<br>`data` — словарь данных | Кастомное действие, заданное в админке |

## 5. Реакция на события показа рекламы

При необходимости приложение может отслеживать события показа рекламы. События для формата Banner описаны ниже, для Stories — в [Stories](stories.md).

+++ Delegate
```swift
func inlineAd(_ ad: InlineAd, didEmit event: InlineAd.Event) {
    switch event {
    case let .banner(bannerEvent):
        switch bannerEvent {
        case let .onBlockView(info):
            break // блок баннеров стал видимым
        case let .onCreativeView(info):
            break // баннер стал видимым
        }
    case .stories:
        break // события Stories описаны в stories.md
    }
}
```
+++ Async
```swift
for await event in inlineAd.events {
    switch event {
    case let .banner(bannerEvent):
        switch bannerEvent {
        case let .onBlockView(info):
            break // блок баннеров стал видимым
        case let .onCreativeView(info):
            break // баннер стал видимым
        }
    case .stories:
        break // события Stories описаны в stories.md
    }
}
```
+++

### События Banner (InlineAd.Event.Banner)

| Тип значения | Параметры | Описание |
|--------------|-----------|---------|
| `onBlockView` | `MultiformatInfo` | Блок баннеров стал видимым на экране |
| `onCreativeView` | `MultiformatInfo` | Конкретный баннер стал видимым на экране |

### Информация о рекламе (MultiformatInfo)

| Поле | Тип | Описание |
|------|-----|----------|
| `slot` | `AdSlot` | Информация о размещении |
| `creativeId` | `String?` | Идентификатор креатива |
| `format` | `String` | Название рекламного формата |

> [!TIP]
> Actions (действия пользователя) обязательны к обработке на интегрирующей стороне. Events (события показа) — информационные и необязательные.

## 6. Провайдер (InlineAdProvider)

`InlineAdProvider` упрощает работу с несколькими рекламными слотами — вместо создания множества `InlineAdLoader` достаточно одного провайдера. Рекомендуется для сценариев с лентой контента и множеством рекламных мест.

### Создание и загрузка

```swift
import MadSDK

let provider = InlineAdProvider()
provider.loadingDelegate = self
provider.delegate = self

// Загрузка одного слота
provider.load(
    InlineAdRequest(slot: AdSlot(padId: "123", position: 0))
)

// Загрузка нескольких слотов
provider.load([
    InlineAdRequest(slot: AdSlot(padId: "123", position: 1)),
    InlineAdRequest(slot: AdSlot(padId: "456", position: 0))
])
```

### Обработка результатов загрузки

```swift
extension ViewController: InlineAdProviderLoadingDelegate {
    func inlineAdProvider(
        _ provider: InlineAdProvider,
        didReceive response: InlineAdProvider.Response,
        for slot: AdSlot
    ) {
        switch response {
        case .success(let slot):
            // Реклама загружена — получить view
            if let adView = provider.view(for: slot, in: self) {
                container.addSubview(adView)
            }
        case .failure(let error, let slot):
            // Ошибка загрузки
            break
        case .noContent(let slot):
            // Нет контента
            break
        }
    }
}
```

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

```swift
extension ViewController: InlineAdProviderDelegate {
    func inlineAdProvider(
        _ provider: InlineAdProvider,
        didEmit event: InlineAd.Event,
        for slot: AdSlot
    ) {
        // Обработка событий показа (аналогично секции 5)
    }

    func inlineAdProvider(
        _ provider: InlineAdProvider,
        didEmit action: InlineAd.Action,
        for slot: AdSlot
    ) {
        // Обработка действий пользователя (аналогично секции 4)
    }
}
```

### Перезагрузка и отмена

```swift
// Перезагрузка конкретного слота
provider.reload(
    InlineAdRequest(slot: AdSlot(padId: "123", position: 0))
)

// Перезагрузка всех загруженных слотов
provider.reload()

// Отмена загрузки конкретного слота
provider.cancel(slot: AdSlot(padId: "123", position: 0))

// Отмена всех активных загрузок
provider.cancelAll()
```

### Возможные состояния (InlineAdProvider.Response)

| Тип значения | Параметры | Описание |
|--------------|-----------|----------|
| `success` | `slot` — информация о размещении | Реклама для слота загружена. Получить view: `provider.view(for: slot)`. |
| `failure` | `error` — причина ошибки, `slot` — информация о размещении | Загрузка завершилась ошибкой. |
| `noContent` | `slot` — информация о размещении | Реклама не была подобрана. |

> [!NOTE]
> `InlineAdProvider` работает только через Delegate API. Для Async-подхода используйте `InlineAdLoader` с `MadsSDK.inLine.load(...)`.

## 7. Пример

Полный пример интеграции InLine рекламы — в [репозитории на GitHub](https://github.com/magnit-tech/mads-ios-sdk).
