---
metadata:
  - name: generator
    content: Diplodoc Platform v5.63.0
alternate:
  - https://ads.yandex.com/helpcenter/ru/dev/ios/swiftui/native.md
  - href: https://ads.yandex.com/helpcenter/ru/dev/ios/swiftui/native.md
    type: text/markdown
    title: Markdown version
  - href: https://ads.yandex.com/helpcenter/ru/dev/ios/llms.txt
    rel: describedby
---
> **Documentation Index:** Fetch the complete configuration index at https://ads.yandex.com/helpcenter/ru/llms.txt

[//]: # (Эта страница переводится также на бразильский португальский — pt-BR)

<!--
Используется в Boost: boost/ru/ad-monetization/dev/ios
-->

# Нативная реклама (SwiftUI)

<!-- source: ru/dev/_includes/native-ads.md -->
Нативная реклама — реклама, внешний вид которой может определяться на стороне приложения. Данная особенность позволяет изменять визуальный стиль объявлений и места их размещения с учетом особенностей дизайна приложения.
<!-- endsource: ru/dev/_includes/native-ads.md -->

<!-- source: ru/dev/_includes/native-ads.md -->
Нативные объявления улучшают впечатления от рекламы, поэтому вы можете показывать больше объявлений, не теряя интерес пользователей. Это позволяет обеспечить максимальный доход от рекламы в долгосрочной перспективе.
<!-- endsource: ru/dev/_includes/native-ads.md -->

<!-- source: ru/dev/_includes/native-ads.md -->
Отрисовка рекламы производится нативными средствами платформы, что увеличивает ее производительность и качество.
<!-- endsource: ru/dev/_includes/native-ads.md -->

{% cut "Внешний вид" %}

<img src="https://yastatic.net/s3/doc-binary/src/docs/support/mobile-ads/ru/monetization/_images/native-ru-ex.png" width="200">

{% endcut %}

Это руководство покажет, как интегрировать нативную рекламу в iOS-приложение на SwiftUI. В дополнение к примерам кода и инструкции оно содержит рекомендации и ссылки на дополнительные ресурсы.

## Пререквизит {#pre}

<!-- source: ru/dev/_includes/pre-ios.md -->
1. Выполните шаги по интеграции SDK, описанные в [Быстром старте](https://ads.yandex.com/helpcenter/ru/dev/ios/quick-start.md).
2. Заранее [проинициализируйте](https://ads.yandex.com/helpcenter/ru/dev/ios/quick-start.md#init) рекламный SDK.
3. Убедитесь, что используете [последнюю версию Yandex Mobile Ads SDK](https://ads.yandex.com/helpcenter/ru/dev/platforms.md), а в случае использования медиации — актуальную [версию единой сборки](https://ads.yandex.com/helpcenter/ru/dev/platforms.md).
<!-- endsource: ru/dev/_includes/pre-ios.md -->

## Имплементация {#implement}

Основные шаги по интеграции нативной рекламы в SwiftUI:

1. Создать `NativeAdState(request:options:)` с запросом `AdRequest` и идентификатором рекламного места. Состояние должно жить дольше одного рендера: держите его в `@StateObject` или во вью-модели.
1. Добавить в иерархию `View` контейнер `NativeAdContainer(state:content:placeholder:)` с этим состоянием.
1. Сверстать карточку внутри контейнера из примитивов `NativeAdAsset` — это единственный способ показать компоненты объявления.
1. Подписаться на события через модификаторы `.onAdLoad`, `.onAdFailure`, `.onAdBindingFailure`, `.onAdClick`, `.onAdImpression`.
1. Передать [дополнительные настройки](https://ads.yandex.com/helpcenter/ru/dev/ios/target-adfox.md), если вы работаете через систему Adfox (через параметры `AdRequest`).
1. Запустить загрузку вызовом `loadAd()`, например в `onAppear`.

## Особенности подключения нативной рекламы {#features}

1. Все вызовы методов Yandex Mobile Ads SDK необходимо выполнять из главного потока. `NativeAdState` и `NativeAdContainer` изолированы на `@MainActor`.

2. Если вы получили ошибку в `.onAdFailure`, не пытайтесь загрузить новое объявление снова. Если это необходимо сделать, ограничьте число повторных попыток загрузки рекламы, чтобы избежать неудачных запросов и проблем с подключением.

3. Одно состояние обслуживает один контейнер. Если второй `NativeAdContainer` получит тот же `NativeAdState`, привязка перейдет к нему: показы и события остаются только у контейнера, который привязался последним, а первый перестает их получать; в лог уходит предупреждение.

4. `NativeAdState` должен создаваться один раз. Держите его в `@StateObject` или во вью-модели, а не создавайте в `body`: контейнер, получивший новое состояние, отвязывает предыдущее объявление и начинает с фазы `.idle`. В ленте `@StateObject` во вью элемента тоже подходит: `LazyVStack` и `List` сохраняют его при уходе элемента с экрана и возврате, реклама не загружается повторно. Если загрузку нужно начинать заранее, до появления элемента на экране, храните состояния во вью-модели.

5. `.onAdLoad` и `.onAdFailure` — события контейнера: они доставляются, только пока контейнер находится в иерархии, и не являются признаком того, что реклама показана. Если результат загрузки нужен независимо от видимости карточки, подпишитесь во вью-модели на `state.$phase`, а на контейнере оставьте `.onAdClick`, `.onAdImpression` и `.onAdBindingFailure`.

6. Компоненты объявления показываются только через примитивы `NativeAdAsset`. Обязательный компонент, для которого примитив не отрисован или скрыт через `.hidden()` либо `.drawingGroup()`, блокирует привязку всего объявления: примерно через две секунды приходит `.onAdBindingFailure` с кодом `NativeErrorCode.noViewForAsset`, а имя компонента лежит в `userInfo["asset_name"]`. Компонент с `.opacity(0)` привязку не ломает, но такой показ помечается как показ с невидимым обязательным компонентом. Поэтому лишний компонент не прячьте, а не рисуйте: `if layout.has(.rating)` вместо `.hidden()` и `.opacity(0)`.

7. Рекомендуется использовать макет, который включает весь набор возможных компонентов. Как показывает практика, макеты, включающие весь набор компонентов, приводят к лучшим конверсиям.

8. Объявления с видео, как правило, имеют более высокий CTR и, соответственно, приносят больше дохода. Для отображения рекламы с видео необходимо, чтобы размер рекламного контейнера и компонента `NativeAdAsset.Media` были не меньше 300x160 dp (density-independent pixels).

9. В `Info.plist` обязателен ключ `SKAdNetworkItems`, иначе загрузка рекламы завершится ошибкой. Подробнее — в разделе [SKAdNetwork](https://ads.yandex.com/helpcenter/ru/dev/ios/skadnetwork.md).

## Загрузка рекламы {#load}

Загрузкой владеет `NativeAdState`. Текущее состояние загрузки публикуется в свойстве `phase`:

* `.idle` — загрузка еще не начиналась;
* `.loading` — загрузка идет;
* `.loaded(NativeAd)` — реклама загружена и может быть показана;
* `.failed(Error)` — загрузка завершилась ошибкой.

Загрузка начинается только по явному вызову `loadAd()`; после ошибки повторный `loadAd()` отправляет запрос еще раз. Чтобы загрузить объявление заново с новыми параметрами, создайте новый `NativeAdState` с нужным `AdRequest`. Это аналог повторного вызова `loadAd(with:)` у `NativeAdLoader` в UIKit.

Параметры запроса за рекламой настраиваются через объект класса `AdRequest`. В качестве параметров запроса нужно передать идентификатор рекламного блока, также дополнительно можно настроить таргетинг и другие данные, способные улучшить качество подбора рекламы. Параметры загрузки изображений передаются через `NativeAdOptions`. Подробнее читайте в разделе [Таргетирование рекламы](https://ads.yandex.com/helpcenter/ru/dev/ios/target.md).

```swift
import SwiftUI
import YandexMobileAds

struct NativeAdView: View {
    @StateObject private var adState = NativeAdState(request: AdRequest(adUnitID: "R-M-XXXXX-YY"))

    var body: some View {
        NativeAdContainer(state: adState) { layout in
            adCard(layout)
        } placeholder: {
            ProgressView()
        }
        .onAdLoad { _ in
            // Объявление успешно загружено
        }
        .onAdFailure { error in
            // Ошибка загрузки
        }
        .onAppear { adState.loadAd() }
    }
}
```

Идентификатор рекламного места (adUnitId) получается в интерфейсе Рекламной сети Яндекса.

## Показ рекламы и верстка карточки {#ad-view}

`NativeAdContainer(state:content:placeholder:)` показывает `placeholder` в фазах `.idle` и `.loading`, карточку — в фазе `.loaded` и ничего — после ошибки. В замыкание `content` приходит `NativeAdLayoutInfo`, который описывает, что есть в объявлении, но не отдает сами значения:

* `has(_:)` — есть ли в объявлении компонент указанного вида;
* `media?.aspectRatio` — соотношение сторон медиа;
* `media?.hasVideo` — содержит ли медиа видео;
* `adType` — тип объявления;
* `warningMinimumArea` — минимальная доля площади карточки, которую должно занимать предупреждение.

Компоненты объявления рисуются примитивами `NativeAdAsset`: `Title`, `Body`, `CallToAction`, `Domain`, `Sponsored`, `Age`, `Price`, `ReviewCount`, `Warning`, `Icon`, `Favicon`, `MainImage`, `Rating`, `Feedback` и `Media`. Каждый примитив читает значение из объявления, отрисовывает его через замыкание `label` и регистрирует получившуюся вью в SDK — так работают валидация показа и обработка клика.

У текстовых примитивов замыкание принимает `String`, у `Icon`, `Favicon`, `MainImage` и `Feedback` — `Image`, у `Rating` — `Double`. `Feedback` показывает иконку по правилу UIKit-кнопки: загруженное изображение из ответа, иначе — иконку SDK. Для всех примитивов, кроме `Rating`, есть инициализатор без аргументов: текст рисуется как `Text`, изображение — как `Image` с `resizable()`, поэтому задайте ему размер через `frame`.

Полный перечень компонентов объявления, обязательных и опциональных, приведен в разделе [Компоненты нативной рекламы](https://ads.yandex.com/helpcenter/ru/dev/ios/components.md).

{% note tip %}

Рекомендуется использовать макет, который включает весь набор возможных компонентов. Как показывает практика, использование такого макета приводит к более высоким конверсиям.

{% endnote %}

Пример карточки:

```swift
private func adCard(_ layout: NativeAdLayoutInfo) -> some View {
    VStack(alignment: .leading, spacing: 8) {
        HStack(spacing: 8) {
            NativeAdAsset.Icon()
                .frame(width: 40, height: 40)
                .clipShape(RoundedRectangle(cornerRadius: 8))
            VStack(alignment: .leading) {
                NativeAdAsset.Title().font(.headline).lineLimit(2)
                HStack(spacing: 4) {
                    NativeAdAsset.Domain().font(.caption).foregroundColor(.secondary)
                    NativeAdAsset.Sponsored().font(.caption2).foregroundColor(.secondary)
                    NativeAdAsset.Age().font(.caption2).foregroundColor(.secondary)
                    NativeAdAsset.Favicon().frame(width: 14, height: 14)
                }
            }
            Spacer()
            NativeAdAsset.Feedback()
                .frame(width: 24, height: 24)
        }

        NativeAdAsset.Media()
            .clipShape(RoundedRectangle(cornerRadius: 12))

        NativeAdAsset.Body().font(.subheadline).lineLimit(3)

        if layout.has(.rating) || layout.has(.reviewCount) || layout.has(.price) {
            HStack(spacing: 8) {
                NativeAdAsset.Rating { value in
                    Text(String(format: "%.1f", value)).font(.caption)
                }
                NativeAdAsset.ReviewCount().font(.caption).foregroundColor(.secondary)
                Spacer()
                NativeAdAsset.Price { text in
                    Text(text).font(.caption).fontWeight(.semibold)
                }
            }
        }

        NativeAdAsset.CallToAction { text in
            Text(text)
                .bold()
                .frame(maxWidth: .infinity, minHeight: 44)
                .background(Color.accentColor)
                .foregroundColor(.white)
                .clipShape(Capsule())
        }

        NativeAdAsset.Warning().font(.caption2).foregroundColor(.secondary)
    }
    .padding(12)
}
```

### Видео в объявлении {#video}

`NativeAdAsset.Media` сохраняет соотношение сторон медиа и показывает видео со стандартными контролами SDK. Если нужны свои контролы, используйте вариант с замыканием `overlay` — в него приходит `NativeAdVideoState` со свойствами `position`, `duration`, `isMuted`, `isMuteControlHidden` и методом `setMuted(_:)`.

```swift
NativeAdAsset.Media { video in
    VStack {
        Spacer()
        HStack {
            Button(video.isMuted ? "Включить звук" : "Выключить звук") {
                video.setMuted(!video.isMuted)
            }
            .opacity(video.isMuteControlHidden ? 0 : 1)
            Spacer()
            Text("\(Int(video.position)) / \(Int(video.duration))")
        }
    }
    .padding(8)
}
```

## События {#events}

События доставляются модификаторами контейнера:

#|
|| **Модификатор** | **Значение в замыкании** | **Когда вызывается** ||
|| `onAdLoad` | `AdInfo?` | Реклама успешно загружена ||
|| `onAdFailure` | `Error` | Загрузка завершилась ошибкой ||
|| `onAdBindingFailure` | `Error` | Загруженную рекламу не удалось связать с отрисованной карточкой ||
|| `onAdClick` | — | Пользователь нажал на объявление ||
|| `onAdImpression` | `ImpressionData?` | Показ засчитан ||
|#

```swift
NativeAdContainer(state: adState) { layout in
    adCard(layout)
} placeholder: {
    ProgressView()
}
.onAdLoad { adInfo in
    // ...
}
.onAdFailure { error in
    // ...
}
.onAdBindingFailure { error in
    // ...
}
.onAdClick {
    // ...
}
.onAdImpression { impressionData in
    // ...
}
```

## Ручная загрузка изображений {#manual-images}

Если изображения нужно загружать самостоятельно, передайте в состояние `NativeAdOptions(shouldLoadImagesAutomatically: false)` и вызовите `loadImages()`. Метод асинхронный: он завершается, когда изображения загружены, и примитивы перерисовываются сами — как завершение `loadImages(completionHandler:)` в UIKit.

```swift
@StateObject private var adState = NativeAdState(
    request: AdRequest(adUnitID: "R-M-XXXXX-YY"),
    options: NativeAdOptions(shouldLoadImagesAutomatically: false)
)

func loadImages() {
    Task { await adState.loadImages() }
}
```

{% note warning %}

Показ засчитывается по видимости карточки и не ждет изображений. До завершения `loadImages()` примитивы `Icon`, `Favicon`, `MainImage` и `Media` рисуют пустое место, поэтому показывайте свой плейсхолдер.

{% endnote %}

## Реклама в ленте {#feed}

В ленте на каждое объявление заводится свой `NativeAdState`. В примере ниже состояния хранятся во вью-модели: так загрузку можно начать заранее, не дожидаясь появления элемента на экране. Элементы `ForEach` различаются по `state.id`. Поддерживаются и `LazyVStack`, и `List`: когда элемент возвращается на экран, SDK заново привязывает рекламу, при этом показ повторно не засчитывается.

```swift
@MainActor
final class FeedViewModel: ObservableObject {
    @Published private(set) var adStates: [NativeAdState] = []

    func addAd() {
        let state = NativeAdState(request: AdRequest(adUnitID: "R-M-XXXXX-YY"))
        adStates.append(state)
        state.loadAd()
    }
}

struct FeedView: View {
    @StateObject private var viewModel = FeedViewModel()

    var body: some View {
        ScrollView {
            LazyVStack(spacing: 16) {
                ForEach(viewModel.adStates) { state in
                    NativeAdContainer(state: state) { layout in
                        adCard(layout)
                    } placeholder: {
                        ProgressView().frame(maxWidth: .infinity, minHeight: 80)
                    }
                    .onAdClick { }
                    .onAdImpression { _ in }
                    .onAdBindingFailure { _ in }
                }
            }
        }
    }
}
```

## Загрузка нескольких рекламных объявлений {#load-more-ads}

Yandex Mobile Ads SDK предоставляет возможность загрузки нескольких рекламных объявлений одним запросом (до девяти объявлений).

{% note info %}

Используйте демоблок `demo-native-bulk-yandex` для `AdUnitID`. Посмотреть поддерживаемые платформы можно на странице [Демоблоки для тестирования](https://ads.yandex.com/helpcenter/ru/dev/ios/demo-blocks.md).

{% endnote %}

1. Создайте экземпляр класса `NativeBulkAdLoader`.

2. Создайте `AdRequest` с идентификатором рекламного блока и `NativeAdOptions` для дополнительных параметров.

3. Вызовите метод `loadAds(with:adsCount:options:)` для загрузки рекламы.

4. Оберните каждое полученное объявление в `NativeAdState(ad:)` и покажите его отдельным `NativeAdContainer`.

```swift
@MainActor
final class BulkViewModel: ObservableObject {
    @Published private(set) var adStates: [NativeAdState] = []

    private let adLoader = NativeBulkAdLoader()

    func loadAds() async {
        let request = AdRequest(adUnitID: "demo-native-bulk-yandex")
        let options = NativeAdOptions()
        do {
            let ads = try await adLoader.loadAds(with: request, adsCount: 3, options: options)
            adStates = ads.map { NativeAdState(ad: $0) }
        } catch {
            // Ошибка загрузки
        }
    }
}
```

{% note info %}

Реклама, переданная в `NativeAdState(ad:)`, считается уже загруженной, поэтому `onAdLoad` для такого состояния не вызывается.

Массив рекламных объявлений, полученный в результате балкового запроса, может содержать от 0 до `adsCount` объектов `NativeAd`. Все полученные объекты рекламы можно показывать независимо друг от друга.

{% endnote %}

## Тестирование интеграции нативной рекламы {#test}

### Использование демоблоков для тестирования рекламы {#demo-blocks}

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

Для гарантированного возврата тестовых объявлений на каждый запрос за рекламой, мы создали специальный демонстрационный идентификатор рекламного места. Используйте его для проверки корректной интеграции рекламы.

Демонстрационный adUnitId для комбинаторной рекламы: `demo-native-content-yandex`.

Демонстрационный adUnitId для рекламы мобильных приложений: `demo-native-app-yandex`.

{% note warning %}

Убедитесь, что перед выкладыванием приложения в store, вы заменили демонстрационный идентификатор рекламного места на настоящий, полученный в интерфейсе Рекламной сети Яндекса.

{% endnote %}

### Проверка корректной интеграции рекламы {#test-int}

<!-- source: ru/dev/_includes/test-integration-ios.md -->
Проверить корректность интеграции рекламы можно через нативный инструмент Console.

Чтобы получить возможность просматривать расширенные логи, необходимо вызвать метод `enableLogging` класса `YandexAds`.

```swift
YandexAds.enableLogging()
```

Для просмотра логов SDK в инструменте Console установите `Subsystem = com.mobile.ads.ads.sdk`. Вы можете фильтровать логи по категории и уровню ошибки.

В случае обнаружения проблем при интеграции рекламы вы увидите подробный отчет о проблемах и рекомендации по их устранению.

<img src= "https://yastatic.net/s3/doc-binary/src/dev/mobile-ads/common/integration-ios-2.png">
<!-- endsource: ru/dev/_includes/test-integration-ios.md -->

## Дополнительные ресурсы {#resources}

* <!-- source: ru/dev/_includes/github-pubdev-links.md -->
  Ссылка на [github](https://github.com/yandexmobile/yandex-ads-sdk-ios).
  <!-- endsource: ru/dev/_includes/github-pubdev-links.md -->
