Skip to main content

Migration Guide from Outbrain SDK to Teads Unified SDK

This guide helps developers currently using the Outbrain SDK transition to the new Teads Unified SDK. Following the merger of Teads and Outbrain, we've created a unified SDK that maintains all current Teads SDK functionality while adding powerful new capabilities.

Important Notice

⚠️ The standalone Outbrain SDK is being deprecated
All Outbrain features are preserved in the Teads Unified SDK
New features and improvements are available
Migration is required but straightforward

Overview of Changes

What's Changing

AspectOutbrain SDKTeads Unified SDK
SDK NameOutbrainSDKTeadsSDK
Import Statementimport OutbrainSDKimport TeadsSDK
InitializationOutbrain.initializeOutbrain()Teads.configure()
Widget ClassSFWidgetTeadsAdPlacementFeed
RecommendationsOBRequestTeadsAdPlacementRecommendations

What's Preserved

✅ All widget functionality
✅ Content recommendation algorithms
✅ Viewability tracking
✅ User personalization
✅ Dark mode support
✅ Event tracking

What's New

🎁 Access to Teads video ad formats
🎁 Unified event system
🎁 Improved performance
🎁 Enhanced privacy controls

Step-by-Step Migration

Step 1: Update Dependencies

Remove Outbrain SDK

CocoaPods:

# Remove from Podfile
# pod 'OutbrainSDK'

# Add Teads SDK
pod 'TeadsSDK', '~> 6.0'

Swift Package Manager:

// Remove Outbrain package
// Add Teads package
dependencies: [
.package(url: "https://github.com/teads/TeadsSDK-iOS.git", upToNextMajor: "6.0.0")
]

Run pod install or update your SPM dependencies.

Step 2: Update Imports

Replace all Outbrain imports with Teads:

// Before
import OutbrainSDK

// After
import TeadsSDK

Step 3: Update SDK Initialization

Before (Outbrain SDK)

// AppDelegate or App initialization
Outbrain.initializeOutbrain(withPartnerKey: "YOUR_PARTNER_KEY")

After (Teads Unified SDK)

// AppDelegate or App initialization
Teads.configure(with: "YOUR_PARTNER_KEY")

Step 4: Migrate Widget Implementation

The widget functionality is now provided through TeadsAdPlacementFeed.

Before (Outbrain SDK) - UIKit

class WidgetViewController: UIViewController {

var widget: SFWidget?

override func viewDidLoad() {
super.viewDidLoad()

// Create widget
widget = SFWidget()
widget?.delegate = self

// Configure widget
widget?.configure(
with: self,
url: "https://mobile-demo.outbrain.com",
widgetId: "MB_1",
widgetIndex: 0,
installationKey: "NANOWDGT01",
userId: "user123",
darkMode: false
)

// Add to view
view.addSubview(widget!)

// Setup constraints
widget?.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
widget!.leadingAnchor.constraint(equalTo: view.leadingAnchor),
widget!.trailingAnchor.constraint(equalTo: view.trailingAnchor),
widget!.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
])
}
}

// Delegate
extension WidgetViewController: SFWidgetDelegate {

func didChangeHeight(_ newHeight: CGFloat) {
// Update height
}

func onRecClick(_ url: URL) {
// Handle click
}

func onOrganicRecClick(_ url: URL) {
// Handle organic click
}
}

After (Teads Unified SDK) - UIKit

class WidgetViewController: UIViewController {

var feedPlacement: TeadsAdPlacementFeed?
var feedView: UIView?

override func viewDidLoad() {
super.viewDidLoad()

// Create configuration
let config = TeadsAdPlacementFeedConfig(
articleUrl: URL(string: "https://mobile-demo.outbrain.com")!,
widgetId: "MB_1",
installationKey: "NANOWDGT01",
widgetIndex: 0,
userId: "user123",
darkMode: false
)

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

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

// Add to view
if let feedView = feedView {
view.addSubview(feedView)

// Setup constraints...
}
} catch {
print("Failed to load feed: \(error)")
}
}
}

// Delegate
extension WidgetViewController: TeadsAdPlacementEventsDelegate {

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

switch event {
case .heightUpdated:
if let height = data?["height"] as? CGFloat {
// No need to update height, the placement handles it's own height automatically
}

case .clickedOrganic:
if let url = data?["url"] as? String {
// Handle organice click
}

case .ready:
print("Feed loaded successfully")

default:
break
}
}
}

Step 5: Migrate SwiftUI Implementation

Before (Outbrain SDK) - SwiftUI

import SwiftUI
import OutbrainSDK

struct ContentView: View {

var body: some View {
VStack {
// Your content

// Outbrain widget
OutbrainWidgetView(
url: "https://mobile-demo.outbrain.com",
widgetId: "MB_1",
widgetIndex: 0,
installationKey: "NANOWDGT01",
userId: "user123",
darkMode: false,
onOrganicRecClick: { url in
// Handle click
}
)
.frame(height: 400)
}
}
}

After (Teads Unified SDK) - SwiftUI

import SwiftUI
import TeadsSDK

struct ContentView: View {
@StateObject private var feedManager = FeedManager()

var body: some View {
VStack {
// Your content

// Teads Feed placement
if let feedView = feedManager.feedView {
FeedContainer(feedView: feedView)
.frame(height: feedManager.feedHeight)
}
}
.onAppear {
feedManager.loadFeed()
}
}
}

// UIViewRepresentable wrapper
struct FeedContainer: UIViewRepresentable {
let feedView: UIView

func makeUIView(context: Context) -> UIView {
return feedView
}

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

// Feed Manager
class FeedManager: NSObject, ObservableObject {
@Published var feedView: UIView?
@Published var feedHeight: CGFloat = 400

private var feedPlacement: TeadsAdPlacementFeed?

func loadFeed() {
let config = TeadsAdPlacementFeedConfig(
articleUrl: URL(string: "https://mobile-demo.outbrain.com")!,
widgetId: "MB_1",
installationKey: "NANOWDGT01",
widgetIndex: 0,
userId: "user123",
darkMode: false
)

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

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

extension FeedManager: 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
}
}
}
}

Step 6: Migrate Recommendations API

Before (Outbrain SDK)

// Fetch recommendations programmatically
let request = OBRequest(url: "https://mobile-demo.outbrain.com",
widgetID: "SDK_1")

Outbran.fetchRecommendations(for: request) { response in
for recommendation in response.recommendations {
print("Title: \(recommendation.title)")
print("URL: \(recommendation.url)")
}
}

After (Teads Unified SDK)

// Fetch recommendations programmatically
let config = TeadsAdPlacementRecommendationsConfig(
articleUrl: URL(string: "https://mobile-demo.outbrain.com")!,
widgetId: "SDK_1",
widgetIndex: 0
)

let placement = TeadsAdPlacementRecommendations(config, delegate: self)

// Callback approach
placement.loadAd { response in
for recommendation in response.recommendations {
print("Title: \(recommendation.title)")
print("URL: \(recommendation.url)")
}
}

// Or async/await approach
Task {
do {
let loader = try placement.loadAd()
let recommendations = try await loader()

for recommendation in recommendations {
print("Title: \(recommendation.title)")
print("URL: \(recommendation.url)")
}
} catch {
print("Error: \(error)")
}
}

Step 7: Update Dark Mode Handling

Before (Outbrain SDK)

// Toggle dark mode
widget?.toggleDarkMode(isDarkMode)

After (Teads Unified SDK)

// For Feed placement
feedPlacement?.toggleDarkMode(isDarkMode)

// Or configure at creation
let config = TeadsAdPlacementFeedConfig(
// ... other parameters
darkMode: true
)

Step 8: Update Viewability Tracking (Regular SDK / TeadsAdPlacementRecommendations)

Before (Outbrain SDK)

// Configure viewability per listing
Outbrain.configureViewabilityPerListing(for: view, withRec: recommendation)

After (Teads Unified SDK)

// Configure viewability per listing
TeadsAdPlacementRecommendations.configureViewabilityPerListing(for: view, withRec: recommendation)

New Opportunities with Teads SDK

Add Video Ads to Your App

Now you can monetize with premium video ads alongside your content recommendations:

class EnhancedContentViewController: UIViewController {

// Existing feed placement
var feedPlacement: TeadsAdPlacementFeed?

// NEW: Add video ads
var videoPlacement: TeadsAdPlacementMedia?

func setupPlacements() {
// Your existing feed
setupFeed()

// NEW: Add video ad
let videoConfig = TeadsAdPlacementMediaConfig(
pid: 84242, // Get PID from Teads
articleUrl: URL(string: "https://example.com/article/123")
)

videoPlacement = TeadsAdPlacementMedia(videoConfig, delegate: self)

if let videoView = try? videoPlacement?.loadAd() {
// Insert video ad in your content
contentStackView.insertArrangedSubview(videoView, at: 2)
}
}
}

Enhanced Event Tracking

The unified event system provides more detailed insights:

extension YourViewController: TeadsAdPlacementEventsDelegate {

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

// Handle specific events
switch event {
case .viewed:
// Track viewability

case .clicked:
// Track engagement

case .failed:
// Handle errors

// ... Handle other events if needed
default:
break
}
}
}

API Mapping Reference

Outbrain SDKTeads Unified SDK
SFWidgetTeadsAdPlacementFeed
OBRequestTeadsAdPlacementRecommendationsConfig
OBRecommendationOBRecommendation (unchanged)
OBErrorStandard Swift Error
SFWidgetDelegateTeadsAdPlacementEventsDelegate
OBResponseDelegateClosure/async callbacks
Outbrain.initializeOutbrain()Teads.configure()
Outbrain.fetchRecommendations()TeadsAdPlacementRecommendations.loadAd()

Support and Resources

Migration Support

Next Steps

  1. Review the Integration Guide for detailed implementation
  2. Explore new Video Ad opportunities
  3. Learn about Privacy Compliance

We're here to help make your migration smooth and successful. See our support page in case you have any questions.