> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cloudx.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Native Ads

> Integrate CloudX native ads and Reels-style native video experiences in iOS apps

Native ads let you render ad creatives using your own UI components — title, body, icon, media, call-to-action — in your own layout. With video creatives and the right playback settings, native ads become full-screen vertical "Reels" (the same UX as Instagram Reels or TikTok).

A Reel is built from three things:

1. **A native ad with video content.** The ad's `mediaView` contains the video player. Other assets (title, body, icon, CTA) are overlaid on top.
2. **Video playback settings.** Three properties on the loader disable fullscreen, start the video with sound, and hide media controls.
3. **A full-screen paging container.** A `UICollectionView` with `pagingEnabled = YES` and a vertical flow layout. Each cell is one Reel.

#### Reels API Surface

| Capability            | CloudX API                            | Description                                               |
| --------------------- | ------------------------------------- | --------------------------------------------------------- |
| Detect video creative | `ad.nativeAd.isVideoContent`          | Returns `YES` when the loaded creative is a video         |
| Get video duration    | `ad.nativeAd.videoDuration`           | Duration of the video in seconds (0 if unknown)           |
| Ad dismissed by user  | `didCloseNativeAd:` delegate callback | Fires when the user reports or hides the ad via AdChoices |
| Disable fullscreen    | `loader.disableVideoFullScreen = YES` | Prevents the video from entering fullscreen on tap        |
| Start unmuted         | `loader.startVideoUnmuted = YES`      | Starts playback with sound on                             |
| Hide media controls   | `loader.hideVideoMediaControls = YES` | Hides play/pause and mute/unmute controls                 |

The Reels video playback settings are adapter-dependent. In CloudX iOS SDK 3.4.0, they are honored by Meta native video creatives.

<Note>
  Native-ad adapter support and dependencies are documented on the adapter pages.
</Note>

#### Create a Loader and Configure Video

<CodeGroup>
  ```objc Objective-C theme={null}
  @interface YourViewController () <CLXNativeAdDelegate, CLXAdRevenueDelegate>
  @property (nonatomic, strong) CLXNativeAdLoader *nativeAdLoader;
  @end

  @implementation YourViewController

  - (void)viewDidLoad {
      [super viewDidLoad];

      self.nativeAdLoader = [[CloudXCore shared] createNativeAdLoaderWithAdUnitIdentifier:@"your-native-ad-unit-id"];
      self.nativeAdLoader.nativeAdDelegate = self;
      self.nativeAdLoader.revenueDelegate = self;

      self.nativeAdLoader.disableVideoFullScreen = YES;
      self.nativeAdLoader.startVideoUnmuted = YES;
      self.nativeAdLoader.hideVideoMediaControls = YES;
  }

  - (void)dealloc {
      [self.nativeAdLoader destroy];
  }

  @end
  ```

  ```swift Swift theme={null}
  class YourViewController: UIViewController, CLXNativeAdDelegate, CLXAdRevenueDelegate {
      private var nativeAdLoader: CLXNativeAdLoader?

      override func viewDidLoad() {
          super.viewDidLoad()

          nativeAdLoader = CloudXCore.shared.createNativeAdLoader(adUnitIdentifier: "your-native-ad-unit-id")
          nativeAdLoader?.nativeAdDelegate = self
          nativeAdLoader?.revenueDelegate = self

          nativeAdLoader?.disableVideoFullScreen = true
          nativeAdLoader?.startVideoUnmuted = true
          nativeAdLoader?.hideVideoMediaControls = true
      }

      deinit {
          nativeAdLoader?.destroy()
      }
  }
  ```
</CodeGroup>

| Property                 | Default | Reels Value | Description                                             |
| ------------------------ | ------- | ----------- | ------------------------------------------------------- |
| `disableVideoFullScreen` | `NO`    | `YES`       | Prevents the video from entering fullscreen when tapped |
| `startVideoUnmuted`      | `NO`    | `YES`       | Starts video playback with sound on                     |
| `hideVideoMediaControls` | `NO`    | `YES`       | Hides play/pause and mute/unmute controls               |

Set these properties **before** calling `loadAd`. They are ignored for static image creatives. For a Reels-style feed, set all three to `YES`.

#### Build a Native Ad Layout

Create a `CLXNativeAdView` using a view binder that maps your custom subviews to asset roles:

<CodeGroup>
  ```objc Objective-C theme={null}
  - (CLXNativeAdView *)createNativeAdView {
      CLXNativeAdViewBinder *binder = [[CLXNativeAdViewBinder alloc] initWithBuilderBlock:^(CLXNativeAdViewBinderBuilder *builder) {
          builder.titleLabelTag = CLXNativeAdViewTagTitleLabel;
          builder.bodyLabelTag = CLXNativeAdViewTagBodyLabel;
          builder.iconImageViewTag = CLXNativeAdViewTagIconImageView;
          builder.callToActionButtonTag = CLXNativeAdViewTagCallToActionButton;
          builder.mediaContentViewTag = CLXNativeAdViewTagMediaViewContainer;
          builder.optionsContentViewTag = CLXNativeAdViewTagOptionsContentView;
          builder.advertiserLabelTag = CLXNativeAdViewTagAdvertiserLabel;
      }];

      CLXNativeAdView *adView = [[CLXNativeAdView alloc] init];
      [adView bindViewsWithViewBinder:binder];
      return adView;
  }
  ```

  ```swift Swift theme={null}
  func createNativeAdView() -> CLXNativeAdView {
      let binder = CLXNativeAdViewBinder { builder in
          builder.titleLabelTag = CLXNativeAdViewTagTitleLabel
          builder.bodyLabelTag = CLXNativeAdViewTagBodyLabel
          builder.iconImageViewTag = CLXNativeAdViewTagIconImageView
          builder.callToActionButtonTag = CLXNativeAdViewTagCallToActionButton
          builder.mediaContentViewTag = CLXNativeAdViewTagMediaViewContainer
          builder.optionsContentViewTag = CLXNativeAdViewTagOptionsContentView
          builder.advertiserLabelTag = CLXNativeAdViewTagAdvertiserLabel
      }

      let adView = CLXNativeAdView()
      adView.bindViews(with: binder)
      return adView
  }
  ```
</CodeGroup>

Alternatively, set outlets directly on the `CLXNativeAdView`:

<CodeGroup>
  ```objc Objective-C theme={null}
  CLXNativeAdView *adView = [[CLXNativeAdView alloc] init];
  adView.titleLabel = myTitleLabel;
  adView.bodyLabel = myBodyLabel;
  adView.iconImageView = myIconImageView;
  adView.callToActionButton = myCTAButton;
  adView.mediaContentView = myMediaContainer;
  adView.optionsContentView = myOptionsContainer;
  adView.advertiserLabel = myAdvertiserLabel;
  ```

  ```swift Swift theme={null}
  let adView = CLXNativeAdView()
  adView.titleLabel = myTitleLabel
  adView.bodyLabel = myBodyLabel
  adView.iconImageView = myIconImageView
  adView.callToActionButton = myCTAButton
  adView.mediaContentView = myMediaContainer
  adView.optionsContentView = myOptionsContainer
  adView.advertiserLabel = myAdvertiserLabel
  ```
</CodeGroup>

#### Load the Ad

**Flow A — Load into a pre-built view:**

<CodeGroup>
  ```objc Objective-C theme={null}
  CLXNativeAdView *adView = [self createNativeAdView];
  [self.nativeAdLoader loadAdIntoAdView:adView];
  ```

  ```swift Swift theme={null}
  let adView = createNativeAdView()
  nativeAdLoader?.loadAd(into: adView)
  ```
</CodeGroup>

**Flow B — Load first, render later (deferred rendering):**

<CodeGroup>
  ```objc Objective-C theme={null}
  [self.nativeAdLoader loadAd];

  - (void)didLoadNativeAd:(nullable CLXNativeAdView *)nativeAdView forAd:(CLXAd *)ad {
      CLXNativeAdView *adView = /* create your ad view */;
      [self.nativeAdLoader renderNativeAdView:adView withAd:ad];
      [self.view addSubview:adView];
  }
  ```

  ```swift Swift theme={null}
  nativeAdLoader?.loadAd()

  func didLoadNativeAd(_ nativeAdView: CLXNativeAdView?, for ad: CLXAd) {
      let adView = /* create your ad view */
      nativeAdLoader?.renderNativeAdView(adView, with: ad)
      view.addSubview(adView)
  }
  ```
</CodeGroup>

#### Handle Callbacks

<CodeGroup>
  ```objc Objective-C theme={null}
  #pragma mark - CLXNativeAdDelegate (Required)

  - (void)didLoadNativeAd:(nullable CLXNativeAdView *)nativeAdView forAd:(CLXAd *)ad {
      NSLog(@"Native ad loaded from %@", ad.networkName);

      if (ad.nativeAd.isVideoContent) {
          NSLog(@"Video duration: %.1fs", ad.nativeAd.videoDuration);
      }

      if (nativeAdView) {
          [self.view addSubview:nativeAdView];
      }
  }

  - (void)didFailToLoadNativeAdForAdUnitIdentifier:(NSString *)adUnitId error:(CLXError *)error {
      NSLog(@"Native ad failed to load: %@", error.localizedDescription);
  }

  - (void)didClickNativeAd:(CLXAd *)ad {
      NSLog(@"Native ad clicked");
  }

  #pragma mark - CLXNativeAdDelegate (Optional)

  - (void)didExpireNativeAd:(CLXAd *)ad {
      NSLog(@"Native ad expired — destroy and reload");
      [self.nativeAdLoader destroyAd:ad];
      [self.nativeAdLoader loadAd];
  }

  - (void)didCloseNativeAd:(CLXAd *)ad {
      NSLog(@"User dismissed the ad via AdChoices");
      [self.nativeAdLoader destroyAd:ad];
  }

  #pragma mark - CLXAdRevenueDelegate

  - (void)didPayRevenueForAd:(CLXAd *)ad {
      NSLog(@"Native ad revenue: %@ from %@", ad.revenue, ad.networkName);
  }
  ```

  ```swift Swift theme={null}
  // MARK: - CLXNativeAdDelegate (Required)

  func didLoadNativeAd(_ nativeAdView: CLXNativeAdView?, for ad: CLXAd) {
      print("Native ad loaded from \(ad.networkName ?? "unknown")")

      if let nativeAd = ad.nativeAd, nativeAd.isVideoContent {
          print("Video duration: \(nativeAd.videoDuration)s")
      }

      if let nativeAdView = nativeAdView {
          view.addSubview(nativeAdView)
      }
  }

  func didFailToLoadNativeAd(forAdUnitIdentifier adUnitId: String, error: CLXError) {
      print("Native ad failed to load: \(error.localizedDescription)")
  }

  func didClickNativeAd(_ ad: CLXAd) {
      print("Native ad clicked")
  }

  // MARK: - CLXNativeAdDelegate (Optional)

  func didExpireNativeAd(_ ad: CLXAd) {
      print("Native ad expired — destroy and reload")
      nativeAdLoader?.destroyAd(ad)
      nativeAdLoader?.loadAd()
  }

  func didCloseNativeAd(_ ad: CLXAd) {
      print("User dismissed the ad via AdChoices")
      nativeAdLoader?.destroyAd(ad)
  }

  // MARK: - CLXAdRevenueDelegate

  func didPayRevenue(for ad: CLXAd) {
      print("Native ad revenue: \(ad.revenue ?? 0) from \(ad.networkName ?? "unknown")")
  }
  ```
</CodeGroup>

#### Clean Up

Always destroy ads when you're done with them:

<CodeGroup>
  ```objc Objective-C theme={null}
  // Destroy a specific loaded ad
  [self.nativeAdLoader destroyAd:ad];

  // Destroy the loader and all associated resources
  [self.nativeAdLoader destroy];
  ```

  ```swift Swift theme={null}
  // Destroy a specific loaded ad
  nativeAdLoader?.destroyAd(ad)

  // Destroy the loader and all associated resources
  nativeAdLoader?.destroy()
  ```
</CodeGroup>

#### Native Ad Assets (CLXNativeAd)

The `CLXNativeAd` object is available via `ad.nativeAd` in delegate callbacks:

| Property                  | Type                | Description                                  |
| ------------------------- | ------------------- | -------------------------------------------- |
| `title`                   | `NSString?`         | Headline text                                |
| `body`                    | `NSString?`         | Body / description text                      |
| `callToAction`            | `NSString?`         | CTA button text (e.g., "Install Now")        |
| `advertiser`              | `NSString?`         | Advertiser name                              |
| `icon`                    | `CLXNativeAdImage?` | App icon image                               |
| `mainImage`               | `CLXNativeAdImage?` | Main image (static creatives)                |
| `mediaView`               | `UIView?`           | Video/media player view (adapter-provided)   |
| `optionsView`             | `UIView?`           | AdChoices or options view (adapter-provided) |
| `mediaContentAspectRatio` | `CGFloat`           | Aspect ratio of the media content            |
| `starRating`              | `NSNumber?`         | App store rating (0–5)                       |
| `isVideoContent`          | `BOOL`              | Whether the creative is a video              |
| `videoDuration`           | `NSTimeInterval`    | Video length in seconds (0 if unknown)       |
| `expired`                 | `BOOL`              | Whether the ad has expired                   |

#### Reels Feed Tips

* Use a `UICollectionView` with `pagingEnabled = YES` and a vertical `UICollectionViewFlowLayout` with `minimumLineSpacing = 0`. Each cell should be full-screen.
* Call `prepareForReuse` on the `CLXNativeAdView` when recycling cells.
* Create one `CLXNativeAdLoader` per slot. Load ads sequentially.
* Destroy ads when no longer needed via `destroyAd:`.
