Skip to main content

Installation

Xcode 26.2

TeadsSDK ships as a binary framework compiled with the Swift 6.2.3 toolchain (Xcode 26.2). Your app must be built with Xcode 26.2 or newer — older toolchains can't link against it.

The framework is built in Swift 5 language mode with library evolution enabled, so it can be consumed from apps using either the Swift 5 or Swift 6 language mode. Adopting Swift 6 in your app is not required.

Installation Methods​

Using CocoaPods​

  1. Add pod 'TeadsSDK', '~> 6.2' into your Podfile.
  2. Run pod install.
  3. Open workspace file and run the project.
platform :ios, '14.0'
use_frameworks!

target 'YourApp' do
pod 'TeadsSDK', '~> 6.2'
end

Implementation Samples​

Sample app

The official Teads SDK sample application demonstrates best practices and integration examples.

TeadsSDK-iOS

SDK Initialization​

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

SwiftUI​

App/Scene/WindowGroup are SwiftUI-only constructs with no Objective-C equivalent — see the UIKit example below for the Objective-C initialization pattern.

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
Teads.testMode = true

Crash Monitoring​

Crash monitoring is enabled by default. To disable:

Teads.isCrashMonitoringEnabled = false

Settings​

Find the full settings list for each placement in the Settings Configuration.

Event Handling​

Use Placement Events. That page is the source for the delegate, event names, and the click-URL rule.

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)

    Swift-only: TeadsAdPlacementSwiftUIView is a generic SwiftUI View, and SwiftUI has no Objective-C equivalent.

    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

    Swift-only: TeadsAdPlacementSwiftUIView is a generic SwiftUI View, and SwiftUI has no Objective-C equivalent.

    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​

Handle .failed on the unified delegate. See Placement Events.

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 Direct integration if you load ads with Teads SDK
  • Review the Mediation guides if Teads is a demand source inside another mediation SDK
  • Review the Prebid guides for header bidding

For additional support, see Support