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
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
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
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:
| Field | Type | Description |
|---|---|---|
title | String? | Ad title |
desc | String? | Ad description |
source | String? | Ad source |
callToAction | String? | Call-to-action text (e.g. "Download") |
imageUrl | String? | Single image URL |
imageUrlList | [String]? | Multi-image URL list (when >1 image) |
iconUrl | String? | Icon URL |
If you need to process data yourself (without using the binder pattern):
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:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
titleTag | Int | Yes | — | Title view tag |
descTag | Int | No | 0 | Description view tag |
sourceTag | Int | No | 0 | Source view tag |
imageTag | Int | No | 0 | Main image view tag |
mediaViewTag | Int | No | 0 | Media view tag (for video ads) |
iconTag | Int | No | 0 | Icon view tag |
callToActionTag | Int | No | 0 | Call-to-action button tag |
logoTag | Int | No | 0 | Ad logo tag |
clickViewTags | [Int] | No | [] | Extra clickable view tag list |
dislikeTag | Int | No | 0 | Dislike button tag |
Best Practices
- Tag uniqueness: When self-rendering, ensure each subview's
tagis unique within the adView scope to avoid conflicts with system tags - Null safety:
NativeAdDatafields are all optional, use?? ""as a fallback - Resource release: Call
destroy()on cell reuse or view removal - Image loading: Download
imageUrl/iconUrlyourself, recommended to use libraries like SDWebImage / Kingfisher
FAQ
Q: Why doesn't the self-rendered view display data?
A: Possible reasons:
taginNativeAdViewBinderdoes not match the actualtagof the viewregisterViewForInteraction(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)
Related Links
- iOS SDK Initialization
- Banner Ad
- Rewarded Video
- Interstitial Ad
- Splash Ad
- API Reference —
NativeAdData/NativeAdViewBinderfull fields
