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.
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.
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
}
}
}
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
}
}
}
}
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)")
}
}
}
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
-
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! -
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 - Problem:
-
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() }
}
}
} -
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
} -
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
- Review the Migration Guide for Teads Users
- Review the Migration Guide for Outbrain Users
- Learn about Privacy & Compliance
For additional support, see Support