Skip to main content

Teads iOS SDK Integration Guide

This guide provides comprehensive instructions for integrating the Teads SDK into your iOS application. Whether you're building a news app, content platform, or any other type of application, this guide will help you implement both video advertising and content recommendations.

info

Before starting: Make sure you've installed the Teads SDK. See the Installation Guide for CocoaPods, Swift Package Manager, or manual installation instructions.

SDK Initialization

Initialize the SDK as early as possible in your app's lifecycle:

SwiftUI

import SwiftUI
import TeadsSDK

@main
struct YourApp: App {
init() {
// Initialize Teads SDK with your partner key
Teads.configure(with: "YOUR_PARTNER_KEY")
}

var body: some Scene {
WindowGroup {
ContentView()
}
}
}

UIKit

import UIKit
import TeadsSDK

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

// Initialize Teads SDK
Teads.configure(with: "YOUR_PARTNER_KEY")

return true
}
}

Global Settings Configuration

The Teads SDK provides global settings that apply across all placements. Configure these settings after SDK initialization.

Test Mode

Enable test mode for detailed logging including viewability percentage and other diagnostic information:

// Enable test mode
TeadsSDK.testMode = true

Crash Monitoring

Crash monitoring is enabled by default. To disable:

TeadsSDK.isCrashMonitoringEnabled = false

Implementation Samples

For complete working examples and sample applications, refer to our public GitHub repositories:

  • iOS Sample App - Complete iOS implementation with all placement types

These repositories contain:

  • Full working applications demonstrating all placement types
  • Best practices and common integration patterns

Media Placement (Video Ads)

Media placements display premium video advertisements within your content.

warning

If you are planning to use Media placement, you should use at least version 6.0.8 of the SDK.

UIKit Implementation

import UIKit
import TeadsSDK

class ArticleViewController: UIViewController {

private var mediaPlacement: TeadsAdPlacementMedia?
private var adView: UIView?

override func viewDidLoad() {
super.viewDidLoad()
setupMediaPlacement()
}

private func setupMediaPlacement() {
// Create configuration
let config = TeadsAdPlacementMediaConfig(
pid: 84242, // Your placement ID
articleUrl: URL(string: "https://example.com/article/123")
)

// Create placement with delegate
mediaPlacement = TeadsAdPlacementMedia(config, delegate: self)

// OR

mediaPlacement = Teads.createPlacement(
with: config,
delegate: self
)

// Load the ad
do {
adView = try mediaPlacement?.loadAd()

// Add to your view hierarchy
if let adView = adView {
insertAdViewInContent(adView)
}
} catch {
print("Failed to load ad: \(error)")
}
}

private func insertAdViewInContent(_ adView: UIView) {
// Add the ad view to your content
// For example, in a scroll view between paragraphs
contentStackView.insertArrangedSubview(adView, at: 2)

// The SDK handles sizing automatically
adView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
adView.leadingAnchor.constraint(equalTo: contentStackView.leadingAnchor),
adView.trailingAnchor.constraint(equalTo: contentStackView.trailingAnchor)
])
}
}

// MARK: - TeadsAdPlacementEventsDelegate
extension ArticleViewController: TeadsAdPlacementEventsDelegate {

func adPlacement(_ placement: TeadsAdPlacementIdentifiable?,
didEmitEvent event: TeadsAdPlacementEventName,
data: [String : Any]?) {

switch event {
case .ready:
print("Ad is ready to be displayed")

case .rendered:
print("Ad has been rendered")

case .viewed:
print("Ad has met viewability criteria")

case .clicked:
print("User clicked on the ad")
// SDK handles URL opening automatically


case .failed:
if let error = data?["error"] as? String {
print("Ad failed to load: \(error)")
}

case .heightUpdated:
if let height = data?["height"] as? CGFloat {
print("Ad height updated to: \(height)")
// No need to do anything, the placement view will handle it's own size.
}

default:
break
}
}
}
Do not open the URL manually

The SDK automatically handles opening the click URL in the .clicked event. Do not implement URL navigation in this event handler — doing so will result in the browser opening twice. Use this event only for tracking/analytics purposes.

SwiftUI Media Implementation (Simplified Approach)

The SDK provides a native SwiftUI view for Media placements:

import SwiftUI
import TeadsSDK

struct ArticleView: View {
let mediaConfig = TeadsAdPlacementMediaConfig(
pid: 84242,
articleUrl: URL(string: "https://example.com/article/123")
)

var body: some View {
ScrollView {
VStack(spacing: 16) {
Text("Article Title")
.font(.largeTitle)

Text("First paragraph of content...")

// Media Ad Placement - Simple one-liner
TeadsAdPlacementSwiftUIView<TeadsAdPlacementMedia>(
config: mediaConfig,
delegate: nil
)
.frame(maxWidth: .infinity) // Important: Set width explicitly

Text("More article content...")
}
.padding()
}
}
}

SwiftUI Media Implementation (With Event Handling)

If you need to handle events or track ad lifecycle:

import SwiftUI
import TeadsSDK

struct ArticleView: View {
@StateObject private var adManager = MediaAdManager()

var body: some View {
ScrollView {
VStack(spacing: 16) {
Text("Article Title")
.font(.largeTitle)

Text("First paragraph of content...")

// Media Ad Placement with event handling
TeadsAdPlacementSwiftUIView<TeadsAdPlacementMedia>(
config: adManager.mediaConfig,
delegate: adManager
)
.frame(maxWidth: .infinity) // Important: Set width explicitly

Text("More article content...")
}
.padding()
}
}
}

// Simple event handler
class MediaAdManager: NSObject, ObservableObject, TeadsAdPlacementEventsDelegate {
@Published var isLoaded = false
@Published var hasError = false

let mediaConfig = TeadsAdPlacementMediaConfig(
pid: 84242,
articleUrl: URL(string: "https://example.com/article/123")
)

func adPlacement(_ placement: TeadsAdPlacementIdentifiable?,
didEmitEvent event: TeadsAdPlacementEventName,
data: [String : Any]?) {

DispatchQueue.main.async {
switch event {
case .ready, .rendered:
self.isLoaded = true
print("Ad loaded successfully")
case .failed:
self.hasError = true
if let error = data?["error"] as? String {
print("Ad failed: \(error)")
}
case .clicked:
print("Ad clicked")
default:
break
}
}
}
}

Feed Placement (Content Recommendations)

Feed placements display content recommendation widget, perfect for keeping users engaged with related content.

UIKit Implementation

import UIKit
import TeadsSDK

class ContentViewController: UIViewController {

private var feedPlacement: TeadsAdPlacementFeed?
private var feedView: UIView?

override func viewDidLoad() {
super.viewDidLoad()
setupFeedPlacement()
}

private func setupFeedPlacement() {
// Create configuration
let config = TeadsAdPlacementFeedConfig(
articleUrl: URL(string: "https://example.com/article/123")!,
widgetId: "MB_1",
installationKey: "NANOWDGT01",
widgetIndex: 0,
userId: nil, // Optional user ID for personalization
darkMode: traitCollection.userInterfaceStyle == .dark
)

// Create placement
feedPlacement = TeadsAdPlacementFeed(config, delegate: self)

// Load the feed
do {
feedView = try feedPlacement?.loadAd()

if let feedView = feedView {
addFeedToView(feedView)
}
} catch {
print("Failed to load feed: \(error)")
}
}

private func addFeedToView(_ feedView: UIView) {
view.addSubview(feedView)

feedView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
feedView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
feedView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16),
feedView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20)
])
}

// Handle dark mode changes
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)

if traitCollection.userInterfaceStyle != previousTraitCollection?.userInterfaceStyle {
// Update dark mode for the feed
feedPlacement?.toggleDarkMode(traitCollection.userInterfaceStyle == .dark)
}
}
}

SwiftUI Feed Implementation (Simplified Approach)

The SDK provides a native SwiftUI view for Feed placements:

import SwiftUI
import TeadsSDK

struct ContentView: View {
let feedConfig = TeadsAdPlacementFeedConfig(
articleUrl: URL(string: "https://example.com/article/123")!,
widgetId: "MB_1",
installationKey: "NANOWDGT01",
widgetIndex: 0,
userId: nil,
darkMode: false
)

var body: some View {
ScrollView {
VStack(spacing: 16) {
Text("Article Title")
.font(.largeTitle)

Text("Article content goes here...")
.padding(.bottom, 20)

// Feed Placement - Simple one-liner
TeadsAdPlacementSwiftUIView<TeadsAdPlacementFeed>(
config: feedConfig,
delegate: nil // Optional delegate
)
.frame(maxWidth: .infinity) // Important: Set width explicitly
}
.padding()
}
}
}

SwiftUI Feed Implementation (With Event Handling)

If you need to handle events or control the Feed placement:

import SwiftUI
import TeadsSDK

struct ContentView: View {
@StateObject private var eventHandler = FeedEventHandler()

let feedConfig = TeadsAdPlacementFeedConfig(
articleUrl: URL(string: "https://example.com/article/123")!,
widgetId: "MB_1",
installationKey: "NANOWDGT01",
widgetIndex: 0,
userId: nil,
darkMode: false
)

var body: some View {
ScrollView {
VStack(spacing: 16) {
Text("Article Title")
.font(.largeTitle)

Text("Article content goes here...")
.padding(.bottom, 20)

// Feed Placement with delegate
TeadsAdPlacementSwiftUIView<TeadsAdPlacementFeed>(
config: feedConfig,
delegate: eventHandler
)
.frame(maxWidth: .infinity) // Important: Set width explicitly

if eventHandler.isLoaded {
Text("Feed loaded successfully!")
.foregroundColor(.green)
}
}
.padding()
}
}
}

// Simple event handler
class FeedEventHandler: NSObject, ObservableObject, TeadsAdPlacementEventsDelegate {
@Published var isLoaded = false
@Published var hasError = false

func adPlacement(_ placement: TeadsAdPlacementIdentifiable?,
didEmitEvent event: TeadsAdPlacementEventName,
data: [String : Any]?) {

DispatchQueue.main.async {
switch event {
case .ready, .rendered:
self.isLoaded = true
case .failed:
self.hasError = true
if let error = data?["error"] as? String {
print("Feed failed: \(error)")
}
case .clickedOrganic:
if let url = data?["url"] as? String {
print("Organic content clicked: \(url)")
// Handle navigation
}
default:
break
}
}
}
}
Important - SwiftUI Width Sizing

Always add .frame(maxWidth: .infinity) to TeadsAdPlacementSwiftUIView to ensure proper width sizing. Without this modifier, the view may render with 0 width even though the height updates correctly.

SwiftUI Dark Mode Support

The Feed placement automatically adapts to the system's color scheme:

import SwiftUI
import TeadsSDK

struct ContentView: View {
@Environment(\.colorScheme) var colorScheme

var body: some View {
VStack {
// Feed automatically uses the current color scheme
TeadsAdPlacementSwiftUIView<TeadsAdPlacementFeed>(
config: TeadsAdPlacementFeedConfig(
articleUrl: URL(string: "https://example.com/article")!,
widgetId: "MB_1",
installationKey: "NANOWDGT01",
widgetIndex: 0,
userId: nil,
darkMode: colorScheme == .dark // Sync with system
),
delegate: nil
)
.frame(maxWidth: .infinity)
}
}
}

For dynamic dark mode toggling after initialization:

struct ContentView: View {
@Environment(\.colorScheme) var colorScheme
@StateObject private var feedViewModel = FeedViewModel()

var body: some View {
VStack {
TeadsAdPlacementSwiftUIView<TeadsAdPlacementFeed>(
config: feedViewModel.config,
delegate: feedViewModel
)
.frame(maxWidth: .infinity)
.onChange(of: colorScheme) { newColorScheme in
// Note: toggleDarkMode requires keeping a reference to the placement
// This is a limitation when using the simplified SwiftUI approach
// For dynamic dark mode, consider using the manual approach with FeedManager
}
}
}
}

// Minimal FeedViewModel for dynamic configuration
class FeedViewModel: NSObject, ObservableObject, TeadsAdPlacementEventsDelegate {
var config: TeadsAdPlacementFeedConfig

init() {
self.config = TeadsAdPlacementFeedConfig(
articleUrl: URL(string: "https://example.com/article")!,
widgetId: "MB_1",
installationKey: "NANOWDGT01",
widgetIndex: 0,
userId: nil,
darkMode: false
)
}

func adPlacement(_ placement: TeadsAdPlacementIdentifiable?,
didEmitEvent event: TeadsAdPlacementEventName,
data: [String : Any]?) {
// Handle events as needed
}
}

Explore More Feature

Option 1: viewWillDisappear

The Feed placement supports an "Explore More" feature that shows additional content when users navigate away:

// Enable explore more when view disappears
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)

feedPlacement?.showExploreMore { [weak self] in
// Called when explore more is dismissed
self?.feedPlacement = nil
}
}

Option 2: Navigation object

In the view model (or other object responsible for navigation) when you detect user tap on the back button to exit the content screen, call the explore more instead of performing the navigation and perform the actual navigation when the explore more is dismissed.

// Enable explore more when user taps back
func backTapped() {
feedPlacement?.showExploreMore { [weak self] in
// Called when explore more is dismissed
// Perform the back navigation
}
}

SwiftUI Implementation

For SwiftUI apps, handling Explore More requires keeping a reference to the placement:

import SwiftUI
import UIKit
import TeadsSDK

// UIViewRepresentable wrapper for the feed view
struct FeedContainer: UIViewRepresentable {
let feedView: UIView

func makeUIView(context: Context) -> UIView {
let containerView = UIView()
containerView.backgroundColor = UIColor.clear

// Add the feed view to the container with proper constraints
containerView.addSubview(feedView)
feedView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
feedView.topAnchor.constraint(equalTo: containerView.topAnchor),
feedView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
feedView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
feedView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor)
])

return containerView
}

func updateUIView(_ uiView: UIView, context: Context) {}
}

struct ArticleView: View {
@StateObject private var feedManager = FeedManagerWithExploreMore()
@Environment(\.dismiss) var dismiss

var body: some View {
ScrollView {
VStack {
// Article content
Text("Article content...")

// Feed placement
if let feedView = feedManager.feedView {
FeedContainer(feedView: feedView)
.frame(maxWidth: .infinity)
.frame(height: feedManager.feedHeight)
}
}
}
.onAppear {
feedManager.loadFeed()
}
.navigationBarBackButtonHidden(true)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button("Back") {
// Show explore more before dismissing
feedManager.showExploreMoreAndDismiss {
dismiss()
}
}
}
}
}
}

class FeedManagerWithExploreMore: NSObject, ObservableObject {
@Published var feedView: UIView?
@Published var feedHeight: CGFloat = 400
private var feedPlacement: TeadsAdPlacementFeed?

func loadFeed() {
let config = TeadsAdPlacementFeedConfig(
articleUrl: URL(string: "https://example.com/article")!,
widgetId: "MB_1",
installationKey: "NANOWDGT01",
widgetIndex: 0
)

feedPlacement = Teads.createPlacement(with: config, delegate: self)

do {
feedView = try feedPlacement?.loadAd()
} catch {
print("Failed to load feed: \(error)")
}
}

func showExploreMoreAndDismiss(completion: @escaping () -> Void) {
feedPlacement?.showExploreMore {
completion()
}
}
}

extension FeedManagerWithExploreMore: TeadsAdPlacementEventsDelegate {
func adPlacement(_ placement: TeadsAdPlacementIdentifiable?,
didEmitEvent event: TeadsAdPlacementEventName,
data: [String : Any]?) {
if event == .heightUpdated, let height = data?["height"] as? CGFloat {
DispatchQueue.main.async {
self.feedHeight = height
}
}
}
}

Media Native Placement

Media Native placements allow you to create custom layouts for video ads that match your app's design.

import UIKit
import TeadsSDK

class NativeAdViewController: UIViewController {

private var nativePlacement: TeadsAdPlacementMediaNative?
private var nativeAdView: TeadsNativeAdView?

override func viewDidLoad() {
super.viewDidLoad()
setupNativeAd()
}

private func setupNativeAd() {
// Create custom native ad view
nativeAdView = createCustomNativeAdView()

// Create configuration
let config = TeadsAdPlacementMediaConfig(
pid: 84242,
articleUrl: URL(string: "https://example.com/article/123")
)

// Create placement
nativePlacement = TeadsAdPlacementMediaNative(config, delegate: self)

// Load and bind the ad
do {
let binder = try nativePlacement?.loadAd()

if let nativeAdView = nativeAdView {
binder?(nativeAdView)
addNativeAdToView(nativeAdView)
}
} catch {
print("Failed to load native ad: \(error)")
}
}

private func createCustomNativeAdView() -> TeadsNativeAdView {
// Create your custom native ad view
// This can be designed in Interface Builder or programmatically
let adView = TeadsNativeAdView(frame: .zero)

// Customize the appearance to match your app
adView.backgroundColor = .systemBackground
adView.layer.cornerRadius = 12
adView.layer.shadowColor = UIColor.black.cgColor
adView.layer.shadowOpacity = 0.1
adView.layer.shadowRadius = 8

return adView
}

private func addNativeAdToView(_ adView: UIView) {
view.addSubview(adView)

adView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
adView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
adView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16),
adView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
adView.heightAnchor.constraint(equalToConstant: 250)
])
}
}

Recommendations API

For programmatic access to content recommendations without UI:

import TeadsSDK

class RecommendationsManager {

private var recommendationsPlacement: TeadsAdPlacementRecommendations?

func fetchRecommendations() async throws -> [OBRecommendation] {
// Create configuration
let config = TeadsAdPlacementRecommendationsURLConfig(
articleUrl: URL(string: "https://example.com/article/123")!,
widgetId: "SDK_1",
widgetIndex: 0
)

// Create placement
recommendationsPlacement = TeadsAdPlacementRecommendations(config, delegate: nil)

// Fetch recommendations
let loader = try recommendationsPlacement?.loadAd()
let recommendations = try await loader?()
return recommendations ?? []
}

// Custom UI for recommendations
func createCustomRecommendationView(for recommendation: OBRecommendation) -> UIView {
let view = UIView()

// Add image
let imageView = UIImageView()
if let imageUrl = URL(string: recommendation.imageUrl ?? "") {
// Load image asynchronously
URLSession.shared.dataTask(with: imageUrl) { data, _, _ in
if let data = data, let image = UIImage(data: data) {
DispatchQueue.main.async {
imageView.image = image
}
}
}.resume()
}

// Add title
let titleLabel = UILabel()
titleLabel.text = recommendation.content
titleLabel.font = .systemFont(ofSize: 16, weight: .semibold)
titleLabel.numberOfLines = 2

// Add source
let sourceLabel = UILabel()
sourceLabel.text = recommendation.source
sourceLabel.font = .systemFont(ofSize: 12)
sourceLabel.textColor = .secondaryLabel

// Layout views...

return view
}
}

Event Handling

extension YourViewController: TeadsAdPlacementEventsDelegate {

func adPlacement(_ placement: TeadsAdPlacementIdentifiable?,
didEmitEvent event: TeadsAdPlacementEventName,
data: [String : Any]?) {

print("Event: \(event) from placement: \(placement?.placementId ?? "unknown")")

switch event {
case .ready:
print("Ad is ready to display")

case .rendered:
print("Ad rendered - send impression to analytics")

case .viewed:
print("Ad is viewable - track viewable impression")

case .clicked:
print("Ad clicked")
// SDK handles URL opening automatically

case .clickedOrganic:
// User clicked on organic content (Feed placement)
if let url = data?["url"] as? String {
print("Organic content clicked: \(url)")
// Perform your content navigation here
}

case .failed:
if let error = data?["error"] as? String {
print("Ad failed to load: \(error)")
}

case .play:
print("Video started playing")

case .pause:
print("Video paused")

case .complete:
print("Video completed")

case .heightUpdated:
if let height = data?["height"] as? CGFloat {
print("Ad height updated to: \(height)")
// No need to do anything, ad placements handle their own layout.
}

case .loaded:
print("Content loaded")

@unknown default:
print("Unknown event: \(event)")
}
}
}
Do not open the URL manually

The SDK automatically handles opening the click URL in the .clicked event. Do not implement URL navigation in this event handler — doing so will result in the browser opening twice. Use this event only for tracking/analytics purposes.

SwiftUI-Specific Considerations

Common Gotchas and Solutions

  1. Width Sizing Issue

    • Problem: Feed/Media shows 0 width even though height updates correctly
    • Solution: Always add .frame(maxWidth: .infinity)
    TeadsAdPlacementSwiftUIView<TeadsAdPlacementFeed>(config: config, delegate: nil)
    .frame(maxWidth: .infinity) // ← Don't forget this!
  2. Dynamic Dark Mode

    • Problem: toggleDarkMode() requires placement reference
    • Solution: Pass dark mode in config for initial state, or use manual approach for dynamic updates
    @Environment(\.colorScheme) var colorScheme
    // Pass current scheme in config
    darkMode: colorScheme == .dark
  3. Explore More Navigation

    • Problem: Need to intercept back navigation for explore more
    • Solution: Use custom back button with showExploreMore() call
    .navigationBarBackButtonHidden(true)
    .toolbar {
    ToolbarItem(placement: .navigationBarLeading) {
    Button("Back") {
    feedManager.showExploreMoreAndDismiss { dismiss() }
    }
    }
    }
  4. Custom UIViewRepresentable

    • Problem: Direct view return causes layout issues
    • Solution: Always wrap in container with constraints
    func makeUIView(context: Context) -> UIView {
    let container = UIView()
    container.addSubview(adView)
    // Add Auto Layout constraints
    return container // ← Return container, not adView
    }
  5. List/LazyVStack Integration

    • Problem: Placements in scrollable lists may have rendering issues
    • Solution: Use fixed frame heights and proper view recycling
    LazyVStack {
    ForEach(items) { item in
    if item.isAd {
    TeadsAdPlacementSwiftUIView<TeadsAdPlacementMedia>(config: config, delegate: nil)
    .frame(maxWidth: .infinity)
    .frame(height: 250) // Fixed height for list items
    }
    }
    }

Best Practices

1. Placement Lifecycle Management

Always maintain strong references to placement objects:

class ViewController: UIViewController {
// ✅ Good: Strong reference maintained
private var mediaPlacement: TeadsAdPlacementMedia?

override func viewDidLoad() {
super.viewDidLoad()

// Create and store placement
mediaPlacement = TeadsAdPlacementMedia(config, delegate: self)
}

deinit {
// Cleanup when done
mediaPlacement = nil
}
}

2. Error Handling

Error handling is optional - ad placements have a height of 0 by default, so in the worst case they simply won't show (in case of Media or Feed placement):

if let adView = try? placement?.loadAd() {
//add adView to your UI
}

3. TableView/CollectionView Integration

UITableView

For table views, no extra work is required - placements handle their own sizing automatically.

UICollectionView

For collection views, use UICollectionViewDelegateFlowLayout to provide dynamic cell sizes:

class YourViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
private var placement: TeadsAdPlacementMedia?
private var adCellHeight: CGFloat = 0.0

override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self

// Create and configure placement
placement = Teads.createPlacement(
with: config,
delegate: self
)
}
}

extension YourViewController: UICollectionViewDelegateFlowLayout {

func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {

let width = collectionView.frame.width

// For the ad cell, return the dynamic height
if indexPath.item == 2 { // Assuming ad is at index 2
return CGSize(width: width, height: adCellHeight)
}

// Return fixed height for other cells
return CGSize(width: width, height: 100.0)
}
}

extension YourViewController: UICollectionViewDataSource {

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10 // Example: 10 items with ad at index 2
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

if indexPath.item == 2 {
// Ad cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "AdCell", for: indexPath)

if let adView = try? placement?.loadAd() {
cell.contentView.subviews.forEach { $0.removeFromSuperview() }
cell.contentView.addSubview(adView)

adView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
adView.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor),
adView.trailingAnchor.constraint(equalTo: cell.contentView.trailingAnchor),
adView.topAnchor.constraint(equalTo: cell.contentView.topAnchor),
adView.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor)
])
}

return cell
} else {
// Regular content cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ContentCell", for: indexPath)
// Configure your content cell...
return cell
}
}
}

extension YourViewController: TeadsAdPlacementEventsDelegate {

func adPlacement(_ placement: TeadsAdPlacementIdentifiable?,
didEmitEvent event: TeadsAdPlacementEventName,
data: [String : Any]?) {

if event == .heightUpdated,
let height = data?["height"] as? CGFloat {

// Update the stored height
adCellHeight = height

// Reload the ad cell to apply new size
let adIndexPath = IndexPath(item: 2, section: 0)
collectionView.performBatchUpdates {
collectionView.reloadItems(at: [adIndexPath])
}
}
}
}

Next Steps


For additional support, see Support