Skip to content

iOS Native Ad Integration

Overview

Native ads use a self-rendering mode: developers draw the ad view themselves, bind views via NativeAdViewBinder, and fill creatives with NativeAdData.

Difference between iOS and Android

iOS self-rendering uses the view.tag pattern to identify components (instead of Android's R.id resource IDs). By setting a unique tag for each subview, and then passing the corresponding tag in NativeAdViewBinder, binding is completed.

Integration Steps

1. Create and load a native ad

swift
import UIKit
import UjuAdCore

final class NativeAdHelper {
    private var nativeAd: UjuAdObject?

    func load(vc: UIViewController, container: UIView) {
        let config = UjuAdConfig(
            placementId: "YOUR_NATIVE_PLACEMENT_ID",
            adViewSize: AdViewSize.nativeSize690x388  // Feed card size
        )
        let native = UjuAdObject.getNativeObject(vc, config: config)
        native.setAdObjectListener(NativeListener(helper: self, container: container, vc: vc))
        self.nativeAd = native
        native.load()
    }

    func destroy() {
        nativeAd?.destroy()
        nativeAd = nil
    }
}

2. Implement the native listener and render

swift
final class NativeListener: FeedAdObjectListener, @unchecked Sendable {
    private weak var helper: NativeAdHelper?
    private weak var container: UIView?
    private weak var vc: UIViewController?

    init(helper: NativeAdHelper, container: UIView, vc: UIViewController) {
        self.helper = helper
        self.container = container
        self.vc = vc
    }

    func onLoadSuccess() {
        guard let nativeAd = helper?.nativeAd, let vc = vc, let container = container else { return }
        // Self-rendering: get creative data and bind views
        renderNativeAd(ad: nativeAd, vc: vc, container: container)
    }

    func onLoadError(error: UjuException) {
        print("Native ad load failed: \(error.description)")
    }

    func onAdShow() {}
    func onAdClicked() {}
    func onAdClosed() {}
    func onLpClosed() {}
    func onAdError(error: UjuException) {
        print("Native ad display error: \(error.description)")
    }

    private func renderNativeAd(ad: UjuAdObject, vc: UIViewController, container: UIView) {
        // See "Self-rendering Example" below
    }
}

Self-rendering Example

swift
private func renderNativeAd(ad: UjuAdObject, vc: UIViewController, container: UIView) {
    // 1. Get creative data
    guard let data = ad.getAdData() else { return }

    // 2. Create views and set a unique tag for each subview
    let adView = UIView()

    let titleLabel = UILabel()
    titleLabel.tag = 101

    let descLabel = UILabel()
    descLabel.tag = 102

    let iconImageView = UIImageView()
    iconImageView.tag = 103

    let mainImageView = UIImageView()
    mainImageView.tag = 104

    let ctaButton = UIButton(type: .system)
    ctaButton.tag = 105

    adView.addSubview(titleLabel)
    adView.addSubview(descLabel)
    adView.addSubview(iconImageView)
    adView.addSubview(mainImageView)
    adView.addSubview(ctaButton)
    // ... set Auto Layout constraints ...

    // 3. Construct binder (view.tag pattern, only titleTag is required, others default to 0 meaning not bound)
    let binder = NativeAdViewBinder(
        titleTag: 101,
        descTag: 102,
        sourceTag: 0,          // 0 means not bound
        imageTag: 104,         // Main image
        mediaViewTag: 0,       // Bind for video creatives
        iconTag: 103,          // Icon
        callToActionTag: 105,
        logoTag: 0,
        clickViewTags: [104],  // Main image is clickable
        dislikeTag: 0
    )

    // 4. Register view for interaction (SDK will auto-fill data + bind click gestures)
    ad.registerViewForInteraction(vc: vc, adView: adView, container: container, binder: binder)
    ad.show(vc, container: container)
}

NativeAdData Fields

UjuAdObject.getAdData() returns NativeAdData?, used for self-rendering data filling. All fields are optional:

FieldTypeDescription
titleString?Ad title
descString?Ad description
sourceString?Ad source
callToActionString?Call-to-action text (e.g. "Download")
imageUrlString?Single image URL
imageUrlList[String]?Multi-image URL list (when >1 image)
iconUrlString?Icon URL

If you need to process data yourself (without using the binder pattern):

swift
if let data = ad.getAdData() {
    print("title: \(data.title ?? "")")
    print("icon: \(data.iconUrl ?? "")")
    // Download and render images yourself
}

NativeAdViewBinder Fields

NativeAdViewBinder is used for self-rendering view binding, using the view.tag pattern. Only titleTag is required; other fields default to 0 (or empty array) meaning not bound:

FieldTypeRequiredDefaultDescription
titleTagIntYesTitle view tag
descTagIntNo0Description view tag
sourceTagIntNo0Source view tag
imageTagIntNo0Main image view tag
mediaViewTagIntNo0Media view tag (for video ads)
iconTagIntNo0Icon view tag
callToActionTagIntNo0Call-to-action button tag
logoTagIntNo0Ad logo tag
clickViewTags[Int]No[]Extra clickable view tag list
dislikeTagIntNo0Dislike button tag

Best Practices

  1. Tag uniqueness: When self-rendering, ensure each subview's tag is unique within the adView scope to avoid conflicts with system tags
  2. Null safety: NativeAdData fields are all optional, use ?? "" as a fallback
  3. Resource release: Call destroy() on cell reuse or view removal
  4. Image loading: Download imageUrl / iconUrl yourself, recommended to use libraries like SDWebImage / Kingfisher

FAQ

Q: Why doesn't the self-rendered view display data?

A: Possible reasons:

  • tag in NativeAdViewBinder does not match the actual tag of the view
  • registerViewForInteraction(vc:adView:container:binder:) was not called to register interaction
  • Container view size is 0 or not added to the view hierarchy
  • getAdData() returns nil (called on non-native type)