> For the complete documentation index, see [llms.txt](https://ios-docs.adster.tech/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ios-docs.adster.tech/ad-types/custom-native-ad.md).

# Custom Native Ad

To receive Custom Native Ads, conform to the `MediationAdDelegate` protocol. Once the ad is loaded, you can populate your custom native ad view with the ad's data.

**Implementation:**

```swift
extension CustomNativeAdManager: MediationAdDelegate {
    func onCustomNativeAdLoaded(customNativeAd: AdsFramework.MediationNativeCustomFormatAd) {
        customNativeAd.eventDelegate = self
        if customNativeAd.getCustomFormatId() == "123456" {
            // Show native custom format for template ID 123456
            displayNativeCustomFormatAd(customNativeAd)
        } else if customNativeAd.getCustomFormatId() == "654321" {
            // Show native custom format for template ID 654321
            displayNativeCustomFormatAd(customNativeAd)
        } else {
            // Fallback for unknown custom native formats
            displayNativeCustomFormatAd(customNativeAd)
        }
    }

    func onAdFailedToLoad(error: AdError) {
        print("Custom Native Ad request failed with reason \(error.description)")
    }
    
    func onAdRevenuePaid(revenue: Double, adUnitId: String, network: String, currency: String, precisionType: AdsFramework.PrecisionType) {
        print("Revenue: \(revenue) \(currency), adUnitId: \(adUnitId), network: \(network), precision: \(precisionType)")
        
    }
}
```

**Define your custom native ad layout:**

The below layout is an example UIKit view. You can create the same UI in a XIB, storyboard, or in code.

```swift
final class CustomNativeAdView: UIView {
    let mainImageView = UIImageView()
    let mediaContainerView = UIView()
    let titleLabel = UILabel()
    let bodyLabel = UILabel()
    let advertiserLabel = UILabel()
    
    let ctaButton = UIButton(type: .system)
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupView()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupView()
    }

    private func setupView() {
        mainImageView.contentMode = .scaleAspectFill
        mainImageView.clipsToBounds = true
        mainImageView.translatesAutoresizingMaskIntoConstraints = false

        mediaContainerView.translatesAutoresizingMaskIntoConstraints = false

        let textStack = UIStackView(arrangedSubviews: [
            titleLabel,
            bodyLabel,
            advertiserLabel,
            ctaButton
        ])
        textStack.axis = .vertical
        textStack.spacing = 8
        textStack.translatesAutoresizingMaskIntoConstraints = false

        let rootStack = UIStackView(arrangedSubviews: [
            mainImageView,
            mediaContainerView,
            textStack
        ])
        rootStack.axis = .vertical
        rootStack.spacing = 12
        rootStack.translatesAutoresizingMaskIntoConstraints = false

        addSubview(rootStack)

        NSLayoutConstraint.activate([
            rootStack.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
            rootStack.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
            rootStack.topAnchor.constraint(equalTo: topAnchor, constant: 16),
            rootStack.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -16),

            mainImageView.heightAnchor.constraint(equalToConstant: 180),
            mediaContainerView.heightAnchor.constraint(equalToConstant: 190)
        ])

        titleLabel.font = .boldSystemFont(ofSize: 18)
        bodyLabel.font = .systemFont(ofSize: 14)
        advertiserLabel.font = .systemFont(ofSize: 13)
        advertiserLabel.textColor = .secondaryLabel
    }
}
```

**Use the `MediationNativeCustomFormatAd` object to populate your custom native layout.:**

The asset names depend on your GAM custom native format. The example below uses common asset names such as `Headline`, `Body`, `Calltoaction`, and `Attribution`.

```swift
private func displayNativeCustomFormatAd(_ ad: AdsFramework.MediationNativeCustomFormatAd) {
    let adView = CustomNativeAdView()

    adView.titleLabel.text = ad.getText(for: "Headline")
    adView.bodyLabel.text = ad.getText(for: "Body")
    adView.advertiserLabel.text = ad.getText(for: "Attribution")
    adView.ctaButton.setTitle(ad.getText(for: "Calltoaction"), for: .normal)

    adView.ctaButton.addAction(UIAction { [weak ad] _ in
        ad?.performClick(on: "Calltoaction")
    }, for: .touchUpInside)

    // Add the custom native view to your container.
    // `adContainerView` is a UIView you have added to your view controller.
    adContainerView.addSubview(adView)
    adView.translatesAutoresizingMaskIntoConstraints = false

    NSLayoutConstraint.activate([
        adView.leadingAnchor.constraint(equalTo: adContainerView.leadingAnchor),
        adView.trailingAnchor.constraint(equalTo: adContainerView.trailingAnchor),
        adView.topAnchor.constraint(equalTo: adContainerView.topAnchor),
        adView.bottomAnchor.constraint(equalTo: adContainerView.bottomAnchor)
    ])

    // Render image and media assets (GAM only)
    renderImageAndMediaAssets(for: ad, into: adView)

    // Record an impression when your custom native ad view is visible.
    ad.recordNativeImpression()
}
```

**Render image and media assets (GAM only):**

Image and `MediaContent` assets are exposed through the `MediationNativeCustomFormatAdGAM` protocol. To access them, import `GoogleMobileAds` and conditionally cast the loaded ad.

```swift
private func renderImageAndMediaAssets(
    for ad: AdsFramework.MediationNativeCustomFormatAd,
    into adView: CustomNativeAdView
) {
    guard let gamAd = ad as? any MediationNativeCustomFormatAdGAM else { return }

    // Image asset (asset names depend on your GAM custom native format,
    // e.g. "Image", "MainImage", "Icon", "Logo")
    if let nativeImage = gamAd.getImage(for: "MainImage") {
        if let uiImage = nativeImage.image {
            adView.mainImageView.image = uiImage
        } else if let url = nativeImage.imageURL {
            // Load remotely using your image loader, e.g. URLSession.
            URLSession.shared.dataTask(with: url) { data, _, _ in
                guard let data, let uiImage = UIImage(data: data) else { return }
                DispatchQueue.main.async {
                    adView.mainImageView.image = uiImage
                }
            }.resume()
        }
    }

    // Video / media content for GAM custom native videos.
    if let mediaContent = gamAd.getMediaContent(),
       mediaContent.hasVideoContent || mediaContent.mainImage != nil {
        let mediaView = MediaView()
        mediaView.contentMode = .scaleAspectFill
        mediaView.clipsToBounds = true
        mediaView.mediaContent = mediaContent
        mediaView.translatesAutoresizingMaskIntoConstraints = false

        adView.mediaContainerView.addSubview(mediaView)
        NSLayoutConstraint.activate([
            mediaView.leadingAnchor.constraint(equalTo: adView.mediaContainerView.leadingAnchor),
            mediaView.trailingAnchor.constraint(equalTo: adView.mediaContainerView.trailingAnchor),
            mediaView.topAnchor.constraint(equalTo: adView.mediaContainerView.topAnchor),
            mediaView.bottomAnchor.constraint(equalTo: adView.mediaContainerView.bottomAnchor)
        ])
    }
}
```

**Tracking Events:**

To track impression and click events for Native Ads, conform to `MediationNativeCustomAdEventDelegate`:

```swift
extension CustomNativeAdManager: MediationNativeCustomAdEventDelegate {
    func recordNativeCustomClick() {
        print("Custom Native ad clicked")
    }
    
    func recordNativeCustomImpression() {
        print("Custom Native ad impression recorded")
    }
    
    func recordNativeCustomClick(ad: AdsFramework.MediationNativeCustomFormatAd, assetName: String) {
        print("Custom native click on asset: \(assetName)")
    }
}
```

**Call `destroy()` when the view controller is deallocated or when the custom native ad is no longer needed.**

```swift
deinit {
    customNativeAd?.destroy()
}
```

{% hint style="info" %}
**If your custom native format can return different asset names, you can inspect the available assets dynamically:**

```swift
let assetNames = customNativeAd.getAvailableAssetNames() ?? []

for assetName in assetNames {
    if let text = customNativeAd.getText(for: assetName), !text.isEmpty {
        print("Text asset: \(assetName) = \(text)")
    }

    if let gamAd = customNativeAd as? any MediationNativeCustomFormatAdGAM,
       let image = gamAd.getImage(for: assetName) {
        print("Image asset: \(assetName), uiImage=\(String(describing: image.image)), url=\(String(describing: image.imageURL))")
    }
}
```

{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://ios-docs.adster.tech/ad-types/custom-native-ad.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
