Нативная реклама

Нативная реклама (Native) — реклама, внешний вид которой может определяться на стороне приложения. Эта особенность позволяет изменять визуальный стиль объявлений и места их размещения с учетом особенностей дизайна приложения.

Примечание

Пример работы всех типов форматов есть в демопроекте.

Сущность

Описание

didFailToLoadWithError

При получении ошибки в func nativeAdLoader(_ loader: NativeAdLoader, didFailLoadingWithError error: Error) не пытайтесь загружать новое объявление снова из этого же метода.

NativeAdRequestConfiguration

В параметрах запроса вы можете указать идентификатор рекламного блока (RM-id), способ загрузки изображения, возраст, пол и другие данные, которые могут повысить релевантность показов. Подробнее читайте в разделе Таргетирование рекламы.

adUnitId

Используйте:

  • development mode — для работы с демоблоками;

  • production mode — для работы с R-M-XXXXXX-Y (уточните реальный ID в интерфейсе Рекламной сети Яндекса). R-M-XXXXXX-Y — это вид рабочего рекламного ID, по которому будут приходить разные креативы.

Пример создания нативной рекламы

Custom Native Ad

Нативная реклама, где UI полностью свой: создается кастомная реализация NativeCustomAdView и задаются внутренние элементы. SDK потом присоединяет к этим элементам компоненты рекламы, клики, трекинг, медиа и т. д.

Как устроено в коде:

  1. NativeCustomViewController:

    • Создается NativeAdLoader.
    • Создается NativeCustomAdView (кастомный UI).
    • Загружается реклама через NativeAdRequestConfiguration(adUnitID:).
    • SDK связывает объект рекламы с кастомной реализацией View с помощью try ad.bind(with: adView).
  2. NativeCustomAdView:

    • Создается UILabel/UIImageView/Button и т. п.
    • Они соотносятся со свойствами SDK (пример можно увидеть в функции bindAssets).
  import UIKit
  import YandexMobileAds

  class NativeCustomViewController: UIViewController {
      private let adUnitID = "demo-native-video-yandex" // Use R-M-XXXXXX-Y or demo-block (look for the description below)
      
      private lazy var adLoader: NativeAdLoader = {
          let loader = NativeAdLoader()
          loader.delegate = self
          return loader
      }()
      
      private let adView: NativeCustomAdView = {
          let adView = NativeCustomAdView()
          adView.translatesAutoresizingMaskIntoConstraints = false
          adView.isHidden = true
          return adView
      }()
      
      override func viewDidLoad() {
          super.viewDidLoad()
          setupUI()
          loadAd()
      }
      
      private func setupUI() {
          view.addSubview(adView)
          
          NSLayoutConstraint.activate([
              adView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
              adView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16),
              adView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
          ])
      }
      
      private func loadAd() {
          print("[NativeCustom] Loading ad...")
          let configuration = NativeAdRequestConfiguration(adUnitID: adUnitID)
          adLoader.loadAd(with: configuration)
      }
  }

  // MARK: - NativeAdLoaderDelegate

  extension NativeCustomViewController: NativeAdLoaderDelegate {
      func nativeAdLoader(_ loader: NativeAdLoader, didLoad ad: NativeAd) {
          print("[NativeCustom] didLoad")
          ad.delegate = self
          
          do {
              try ad.bind(with: adView)
              adView.isHidden = false
          } catch {
              print("[NativeCustom] Binding error: \(error)")
          }
      }
      
      func nativeAdLoader(_ loader: NativeAdLoader, didFailLoadingWithError error: Error) {
          print("[NativeCustom] didFailLoading: \(error)")
      }
  }

  // MARK: - NativeAdDelegate

  extension NativeCustomViewController: NativeAdDelegate {
      func nativeAdDidClick(_ ad: NativeAd) {
          print("[NativeCustom] didClick")
      }
      
      func nativeAdWillLeaveApplication(_ ad: NativeAd) {
          print("[NativeCustom] willLeaveApplication")
      }
      
      func nativeAd(_ ad: NativeAd, willPresentScreen viewController: UIViewController?) {
          print("[NativeCustom] willPresentScreen")
      }
      
      func nativeAd(_ ad: NativeAd, didTrackImpressionWith impressionData: ImpressionData?) {
          print("[NativeCustom] didTrackImpression: \(impressionData?.rawData ?? "nil")")
      }
      
      func nativeAd(_ ad: NativeAd, didDismissScreen viewController: UIViewController?) {
          print("[NativeCustom] didDismissScreen")
      }
      
      func close(_ ad: NativeAd) {
          print("[NativeCustom] close")
      }
  }
  import UIKit
  import YandexMobileAds

  class NativeCustomAdView: YMANativeAdView {
      
      // Обязательные компоненты (Да)
      
      // Заголовок — обязательный
      private let titleLabel_: UILabel = {
          let label = UILabel()
          label.font = .systemFont(ofSize: 16, weight: .bold)
          label.numberOfLines = 2
          label.translatesAutoresizingMaskIntoConstraints = false
          return label
      }()
      
      // Домен — обязательный
      private let domainLabel_: UILabel = {
          let label = UILabel()
          label.font = .systemFont(ofSize: 12)
          label.textColor = .tertiaryLabel
          label.translatesAutoresizingMaskIntoConstraints = false
          return label
      }()
      
      // Предупреждение — обязательный
      private let warningLabel_: UILabel = {
          let label = UILabel()
          label.font = .systemFont(ofSize: 10)
          label.textColor = .systemOrange
          label.numberOfLines = 0
          label.translatesAutoresizingMaskIntoConstraints = false
          return label
      }()
      
      // Рекламная и возрастная метка — обязательный
      private let sponsoredLabel_: UILabel = {
          let label = UILabel()
          label.font = .systemFont(ofSize: 10)
          label.textColor = .tertiaryLabel
          label.translatesAutoresizingMaskIntoConstraints = false
          return label
      }()
      
      // Значок меню (feedback) — обязательный
      private let feedbackButton_: UIButton = {
          let button = UIButton(type: .system)
          button.translatesAutoresizingMaskIntoConstraints = false
          return button
      }()
      
      // Кнопка действия — обязательный
      private let callToActionButton_: UIButton = {
          let button = UIButton(type: .system)
          button.backgroundColor = .systemBlue
          button.setTitleColor(.white, for: .normal)
          button.layer.cornerRadius = 8
          button.translatesAutoresizingMaskIntoConstraints = false
          return button
      }()
      
      // Медиа — обязательный
      private let mediaView_: YMANativeMediaView = {
          let mediaView = YMANativeMediaView()
          mediaView.translatesAutoresizingMaskIntoConstraints = false
          return mediaView
      }()
      
      // Обязательные для рекламы приложений
      
      // Иконка приложения — обязательный для рекламы приложений
      private let iconImageView_: UIImageView = {
          let imageView = UIImageView()
          imageView.contentMode = .scaleAspectFit
          imageView.layer.cornerRadius = 8
          imageView.clipsToBounds = true
          imageView.translatesAutoresizingMaskIntoConstraints = false
          return imageView
      }()
      
      // Цена — обязательный для рекламы приложений
      private let priceLabel_: UILabel = {
          let label = UILabel()
          label.font = .systemFont(ofSize: 14, weight: .semibold)
          label.translatesAutoresizingMaskIntoConstraints = false
          return label
      }()
      
      // Необязательные компоненты (Нет)
      
      // Фавиконка — необязательный
      private let faviconImageView_: UIImageView = {
          let imageView = UIImageView()
          imageView.contentMode = .scaleAspectFit
          imageView.translatesAutoresizingMaskIntoConstraints = false
          return imageView
      }()
      
      // Количество оценок — необязательный
      private let reviewCountLabel_: UILabel = {
          let label = UILabel()
          label.font = .systemFont(ofSize: 12)
          label.textColor = .secondaryLabel
          label.translatesAutoresizingMaskIntoConstraints = false
          return label
      }()
      
      // Рейтинг — необязательный
      private let ratingView_: UIView = {
          let view = UIView()
          view.translatesAutoresizingMaskIntoConstraints = false
          return view
      }()
      
      // Основной текст — необязательный
      private let bodyLabel_: UILabel = {
          let label = UILabel()
          label.font = .systemFont(ofSize: 14)
          label.numberOfLines = 3
          label.textColor = .secondaryLabel
          label.translatesAutoresizingMaskIntoConstraints = false
          return label
      }()
      
      override init(frame: CGRect) {
          super.init(frame: frame)
          setupUI()
          bindAssets()
      }
      
      required init?(coder: NSCoder) {
          super.init(coder: coder)
          setupUI()
          bindAssets()
      }
      
      private func setupUI() {
          backgroundColor = .secondarySystemBackground
          layer.cornerRadius = 12
          
          addSubview(iconImageView_)
          addSubview(titleLabel_)
          addSubview(feedbackButton_)
          addSubview(sponsoredLabel_)
          addSubview(domainLabel_)
          addSubview(bodyLabel_)
          addSubview(warningLabel_)
          addSubview(mediaView_)
          addSubview(faviconImageView_)
          addSubview(ratingView_)
          addSubview(reviewCountLabel_)
          addSubview(priceLabel_)
          addSubview(callToActionButton_)
          
          NSLayoutConstraint.activate([
              // Иконка (слева сверху)
              iconImageView_.topAnchor.constraint(equalTo: topAnchor, constant: 12),
              iconImageView_.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12),
              iconImageView_.widthAnchor.constraint(equalToConstant: 50),
              iconImageView_.heightAnchor.constraint(equalToConstant: 50),
              
              // Заголовок (справа от иконки)
              titleLabel_.topAnchor.constraint(equalTo: topAnchor, constant: 12),
              titleLabel_.leadingAnchor.constraint(equalTo: iconImageView_.trailingAnchor, constant: 12),
              titleLabel_.trailingAnchor.constraint(equalTo: feedbackButton_.leadingAnchor, constant: -8),
              
              // Feedback кнопка (справа сверху)
              feedbackButton_.topAnchor.constraint(equalTo: topAnchor, constant: 12),
              feedbackButton_.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -12),
              feedbackButton_.widthAnchor.constraint(equalToConstant: 24),
              feedbackButton_.heightAnchor.constraint(equalToConstant: 24),
              
              // Рекламная метка (под заголовком)
              sponsoredLabel_.topAnchor.constraint(equalTo: titleLabel_.bottomAnchor, constant: 4),
              sponsoredLabel_.leadingAnchor.constraint(equalTo: titleLabel_.leadingAnchor),
              
              // Домен (под рекламной меткой)
              domainLabel_.topAnchor.constraint(equalTo: sponsoredLabel_.bottomAnchor, constant: 2),
              domainLabel_.leadingAnchor.constraint(equalTo: titleLabel_.leadingAnchor),
              
              // Фавиконка (рядом с доменом)
              faviconImageView_.centerYAnchor.constraint(equalTo: domainLabel_.centerYAnchor),
              faviconImageView_.leadingAnchor.constraint(equalTo: domainLabel_.trailingAnchor, constant: 4),
              faviconImageView_.widthAnchor.constraint(equalToConstant: 16),
              faviconImageView_.heightAnchor.constraint(equalToConstant: 16),
              
              // Основной текст (под иконкой)
              bodyLabel_.topAnchor.constraint(equalTo: iconImageView_.bottomAnchor, constant: 12),
              bodyLabel_.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12),
              bodyLabel_.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -12),
              
              // Предупреждение (под основным текстом)
              warningLabel_.topAnchor.constraint(equalTo: bodyLabel_.bottomAnchor, constant: 8),
              warningLabel_.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12),
              warningLabel_.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -12),
              
              // Медиа (под предупреждением)
              mediaView_.topAnchor.constraint(equalTo: warningLabel_.bottomAnchor, constant: 12),
              mediaView_.leadingAnchor.constraint(equalTo: leadingAnchor),
              mediaView_.trailingAnchor.constraint(equalTo: trailingAnchor),
              mediaView_.heightAnchor.constraint(equalTo: mediaView_.widthAnchor, multiplier: 9.0/16.0),
              
              // Рейтинг (под медиа)
              ratingView_.topAnchor.constraint(equalTo: mediaView_.bottomAnchor, constant: 12),
              ratingView_.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12),
              ratingView_.heightAnchor.constraint(equalToConstant: 16),
              ratingView_.widthAnchor.constraint(equalToConstant: 80),
              
              // Количество оценок (рядом с рейтингом)
              reviewCountLabel_.centerYAnchor.constraint(equalTo: ratingView_.centerYAnchor),
              reviewCountLabel_.leadingAnchor.constraint(equalTo: ratingView_.trailingAnchor, constant: 8),
              
              // Цена (справа)
              priceLabel_.centerYAnchor.constraint(equalTo: ratingView_.centerYAnchor),
              priceLabel_.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -12),
              
              // Кнопка действия (внизу)
              callToActionButton_.topAnchor.constraint(equalTo: ratingView_.bottomAnchor, constant: 12),
              callToActionButton_.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12),
              callToActionButton_.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -12),
              callToActionButton_.heightAnchor.constraint(equalToConstant: 44),
              callToActionButton_.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -12)
          ])
      }
      
      private func bindAssets() {
          // Обязательные (Да)
          titleLabel = titleLabel_
          domainLabel = domainLabel_
          warningLabel = warningLabel_
          sponsoredLabel = sponsoredLabel_
          feedbackButton = feedbackButton_
          callToActionButton = callToActionButton_
          mediaView = mediaView_
          
          // Обязательные для рекламы приложений
          iconImageView = iconImageView_
          priceLabel = priceLabel_
          
          // Необязательные (Нет)
          faviconImageView = faviconImageView_
          reviewCountLabel = reviewCountLabel_
          ratingView = ratingView_
          bodyLabel = bodyLabel_
      }
  }
Native Template Ad

Нативная реклама, которую отрисовывает SDK внутри готовой NativeBannerView. После создания NativeAd SDK сам наполняет и показывает рекламу.

NativeTemplateViewController создает:

  • NativeAdLoader — загрузчик;
  • NativeBannerView — готовый шаблонный UI-контейнер.
    import UIKit
    import YandexMobileAds

    class NativeTemplateViewController: UIViewController {
        private let adUnitID = "ad-unit-ID" // Use R-M-XXXXXX-Y or "demo-native-content-yandex" (look for the description below)
        
        private lazy var adLoader: NativeAdLoader = {
            let loader = NativeAdLoader()
            loader.delegate = self
            return loader
        }()
        
        private let adView: NativeBannerView = {
            let adView = NativeBannerView()
            adView.translatesAutoresizingMaskIntoConstraints = false
            return adView
        }()
        
        override func viewDidLoad() {
            super.viewDidLoad()
            setupUI()
            loadAd()
        }
        
        private func setupUI() {
            view.addSubview(adView)
            
            NSLayoutConstraint.activate([
                adView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
                adView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10),
                adView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -10)
            ])
        }
        
        private func loadAd() {
            print("[NativeTemplate] Loading ad...")
            let configuration = NativeAdRequestConfiguration(adUnitID: adUnitID)
            adLoader.loadAd(with: configuration)
        }
    }

    // MARK: - NativeAdLoaderDelegate

    extension NativeTemplateViewController: NativeAdLoaderDelegate {
        func nativeAdLoader(_ loader: NativeAdLoader, didLoad ad: NativeAd) {
            print("[NativeTemplate] didLoad")
            ad.delegate = self
            adView.ad = ad
        }
        
        func nativeAdLoader(_ loader: NativeAdLoader, didFailLoadingWithError error: Error) {
            print("[NativeTemplate] didFailLoading: \(error)")
        }
    }

    // MARK: - NativeAdDelegate

    extension NativeTemplateViewController: NativeAdDelegate {
        func nativeAdDidClick(_ ad: NativeAd) {
            print("[NativeTemplate] didClick")
        }
        
        func nativeAdWillLeaveApplication(_ ad: NativeAd) {
            print("[NativeTemplate] willLeaveApplication")
        }
        
        func nativeAd(_ ad: NativeAd, willPresentScreen viewController: UIViewController?) {
            print("[NativeTemplate] willPresentScreen")
        }
        
        func nativeAd(_ ad: NativeAd, didTrackImpressionWith impressionData: ImpressionData?) {
            print("[NativeTemplate] didTrackImpression: \(impressionData?.rawData ?? "nil")")
        }
        
        func nativeAd(_ ad: NativeAd, didDismissScreen viewController: UIViewController?) {
            print("[NativeTemplate] didDismissScreen")
        }
        
        func close(_ ad: NativeAd) {
            print("[NativeTemplate] close")
        }
    }

Обязательные компоненты нативной рекламы

Компонент в объявлении

Компонент

Тип

Обязательность

Заголовок

titleLabel

UILabel

Да

Домен

domainLabel

UILabel

Да

Предупреждение

warningLabel

UILabel

Да

Рекламная и возрастная метка

sponsoredLabel

UILabel

Да

Значок меню

feedbackButton

UIButton

Да

Кнопка действия

callToActionButton

UIButton

Да

Медиа

mediaView

YMANativeMediaView

Да

Иконка приложения

iconImageView

UIImageView

Да, для рекламы приложений

Цена

priceLabel

UILabel

Да, для рекламы приложений

Фавиконка

faviconImageView

UIImageView

Нет

Количество оценок

reviewCountLabel

UILabel

Нет

Рейтинг

ratingView

UIView

Нет

Основной текст

bodyLabel

UILabel

Нет