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
| Aspect | Outbrain SDK | Teads Unified SDK |
|---|---|---|
| SDK Name | OutbrainSDK | TeadsSDK |
| Import Statement | import OutbrainSDK | import TeadsSDK |
| Initialization | Outbrain.initializeOutbrain() | Teads.configure() |
| Widget Class | SFWidget | TeadsAdPlacementFeed |
| Recommendations | OBRequest | TeadsAdPlacementRecommendations |
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.2'
Swift Package Manager:
// Remove Outbrain package
// Add Teads package
dependencies: [
.package(url: "https://github.com/teads/TeadsSDK-iOS.git", .upToNextMajor(from: "6.2.1"))
]
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)
- Swift
- Objective-C
// AppDelegate or App initialization
Teads.configure(with: "YOUR_PARTNER_KEY")
// AppDelegate or App initialization
[Teads configureWith:@"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
- Swift
- Objective-C
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 organic click
}
case .ready:
print("Feed loaded successfully")
default:
break
}
}
}
// TeadsAdPlacementFeedConfig is a Swift struct and Teads.createPlacement(with:delegate:)
// is a Swift generic — neither bridges to Objective-C. Use the convenience initializer
// and getAdView() (instead of loadAd(), which returns a non-bridgeable existential) instead.
@interface WidgetViewController : UIViewController <TeadsAdPlacementEventsDelegate>
@property (nonatomic, strong, nullable) TeadsAdPlacementFeed *feedPlacement;
@property (nonatomic, strong, nullable) UIView *feedView;
@end
@implementation WidgetViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Create placement
NSURL *articleUrl = [NSURL URLWithString:@"https://mobile-demo.outbrain.com"];
self.feedPlacement = [[TeadsAdPlacementFeed alloc] initWithArticleUrl:articleUrl
widgetId:@"MB_1"
installationKey:@"NANOWDGT01"
widgetIndex:0
userId:@"user123"
darkMode:NO
extId:nil
extSecondaryId:nil
obPubImp:nil
delegate:self];
// Load feed
self.feedView = [self.feedPlacement getAdView];
// Add to view
if (self.feedView) {
[self.view addSubview:self.feedView];
// Setup constraints...
}
}
@end
// Delegate
@implementation WidgetViewController (TeadsAdPlacementEventsDelegate)
- (void)adPlacement:(id<TeadsAdPlacementIdentifiable>)placement
didEmitEvent:(TeadsAdPlacementEventName)event
data:(NSDictionary<NSString *, id> *)data {
switch (event) {
case TeadsAdPlacementEventNameHeightUpdated: {
NSNumber *height = data[@"height"];
if (height) {
// No need to update height, the placement handles its own height automatically
}
break;
}
case TeadsAdPlacementEventNameClickedOrganic: {
NSString *url = data[@"url"];
if (url) {
// Handle organic click
}
break;
}
case TeadsAdPlacementEventNameReady:
NSLog(@"Feed loaded successfully");
break;
default:
break;
}
}
@end
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.
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
Swift-only: TeadsAdPlacementSwiftUIView is a generic SwiftUI View, and SwiftUI has no Objective-C equivalent.
import SwiftUI
import TeadsSDK
struct ContentView: View {
var body: some View {
VStack {
// Your content
// Teads Feed placement - Simple one-liner
TeadsAdPlacementSwiftUIView<TeadsAdPlacementFeed>(
config: TeadsAdPlacementFeedConfig(
articleUrl: URL(string: "https://mobile-demo.outbrain.com")!,
widgetId: "MB_1",
installationKey: "NANOWDGT01",
widgetIndex: 0,
userId: "user123",
darkMode: false
),
delegate: nil // Add delegate if you need event handling
)
.frame(maxWidth: .infinity) // Important: Set width explicitly
}
}
}
Step 6: Migrate Recommendations API
Before (Outbrain SDK)
// Fetch recommendations programmatically
let request = OBRequest(url: "https://mobile-demo.outbrain.com",
widgetID: "SDK_1")
Outbrain.fetchRecommendations(for: request) { response in
for recommendation in response.recommendations {
print("Title: \(recommendation.content)")
print("URL: \(recommendation.url)")
}
}
After (Teads Unified SDK)
- Swift
- Objective-C
// 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.content)")
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.content)")
print("URL: \(recommendation.url)")
}
} catch {
print("Error: \(error)")
}
}
// TeadsAdPlacementRecommendationsConfig is a protocol (its concrete conforming struct,
// e.g. TeadsAdPlacementRecommendationsURLConfig, is a Swift struct) — neither bridges to
// Objective-C. Use the convenience initializer instead.
NSURL *articleUrl = [NSURL URLWithString:@"https://mobile-demo.outbrain.com"];
TeadsAdPlacementRecommendations *placement = [[TeadsAdPlacementRecommendations alloc] initWithArticleUrl:articleUrl
widgetId:@"SDK_1"
widgetIndex:0
externalID:nil
delegate:self];
// Callback approach
[placement loadAdWithCompletion:^(OBRecommendationResponse *response) {
for (OBRecommendation *recommendation in response.recommendations) {
NSLog(@"Title: %@", recommendation.content);
NSLog(@"URL: %@", recommendation.url);
}
}];
// Note: the async/await loadAd() variant is Swift-only (async doesn't bridge to Objective-C).
Step 7: Update Dark Mode Handling
Before (Outbrain SDK)
// Toggle dark mode
widget?.toggleDarkMode(isDarkMode)
After (Teads Unified SDK)
- Swift
- Objective-C
// For Feed placement
feedPlacement?.toggleDarkMode(isDarkMode)
// Or configure at creation
let config = TeadsAdPlacementFeedConfig(
// ... other parameters
darkMode: true
)
// For Feed placement
[feedPlacement toggleDarkMode:isDarkMode];
// Or configure at creation — TeadsAdPlacementFeedConfig is a Swift struct and doesn't
// bridge to Objective-C; pass darkMode:YES directly to the convenience initializer
// instead (see the Feed placement examples earlier in this guide).
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)
- Swift
- Objective-C
// Configure viewability per listing
TeadsAdPlacementRecommendations.configureViewabilityPerListing(for: view, withRec: recommendation)
// Configure viewability per listing
[TeadsAdPlacementRecommendations configureViewabilityPerListingFor: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:
- Swift
- Objective-C
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)
}
}
}
// TeadsAdPlacementMediaConfig is a Swift struct and doesn't bridge to Objective-C;
// use the convenience initializer instead.
@interface EnhancedContentViewController : UIViewController
@property (nonatomic, strong, nullable) TeadsAdPlacementFeed *feedPlacement; // Existing feed placement
@property (nonatomic, strong, nullable) TeadsAdPlacementMedia *videoPlacement; // NEW: Add video ads
@end
@implementation EnhancedContentViewController
- (void)setupPlacements {
// Your existing feed
[self setupFeed];
// NEW: Add video ad
NSURL *articleUrl = [NSURL URLWithString:@"https://example.com/article/123"];
self.videoPlacement = [[TeadsAdPlacementMedia alloc] initWithPid:84242 // Get PID from Teads
articleUrl:articleUrl
delegate:self];
NSError *error = nil;
UIView *videoView = [self.videoPlacement loadAdAndReturnError:&error];
if (videoView) {
// Insert video ad in your content
[self.contentStackView insertArrangedSubview:videoView atIndex:2];
}
}
@end
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
// SDK handles URL opening automatically
case .failed:
// Handle errors
// ... Handle other events if needed
default:
break
}
}
}
API Mapping Reference
| Outbrain SDK | Teads Unified SDK |
|---|---|
SFWidget | TeadsAdPlacementFeed |
OBRequest | TeadsAdPlacementRecommendationsConfig |
OBRecommendation | OBRecommendation (unchanged) |
OBError | Standard Swift Error |
SFWidgetDelegate | TeadsAdPlacementEventsDelegate |
OBResponseDelegate | Closure/async callbacks |
Outbrain.initializeOutbrain() | Teads.configure() |
Outbrain.fetchRecommendations() | TeadsAdPlacementRecommendations.loadAd() |
Outbrain.setTestMode(value) | Teads.testMode = value |
Support and Resources
Migration Support
- Integration Guide
- Support: Support
Next Steps
- Review the Integration Guide for detailed implementation
- Explore new Video Ad opportunities
- 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.