Recommendations API
Recommendations API
For programmatic access to content recommendations without UI:
- Swift
- Objective-C
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...
// Configure viewability tracking — required for accurate impression reporting
TeadsAdPlacementRecommendations.configureViewabilityPerListing(
for: view,
withRec: recommendation
)
return view
}
}
#import <TeadsSDK/TeadsSDK-Swift.h>
@interface RecommendationsManager : NSObject
@property (nonatomic, strong) TeadsAdPlacementRecommendations *recommendationsPlacement;
@end
@implementation RecommendationsManager
- (void)fetchRecommendationsWithCompletion:(void (^)(NSArray<OBRecommendation *> *recommendations))completion {
// Objective-C has no separate Config type for Recommendations — create the
// placement directly with its Objective-C-friendly convenience initializer
self.recommendationsPlacement = [[TeadsAdPlacementRecommendations alloc] initWithArticleUrl:[NSURL URLWithString:@"https://example.com/article/123"]
widgetId:@"SDK_1"
widgetIndex:0
externalID:nil
delegate:nil];
// loadAd(completion:) is used instead of the Swift-only async
// `loadAd() throws -> () async throws -> [OBRecommendation]`
[self.recommendationsPlacement loadAdWithCompletion:^(OBRecommendationResponse *response) {
completion(response.recommendations ?: @[]);
}];
}
// Custom UI for recommendations
- (UIView *)createCustomRecommendationViewFor:(OBRecommendation *)recommendation {
UIView *view = [[UIView alloc] init];
// Add image
UIImageView *imageView = [[UIImageView alloc] init];
NSURL *imageUrl = recommendation.image.url;
if (imageUrl) {
// Load image asynchronously
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:imageUrl
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (data) {
UIImage *image = [UIImage imageWithData:data];
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
imageView.image = image;
});
}
}
}];
[task resume];
}
// Add title
UILabel *titleLabel = [[UILabel alloc] init];
titleLabel.text = recommendation.content;
titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightSemibold];
titleLabel.numberOfLines = 2;
// Add source
UILabel *sourceLabel = [[UILabel alloc] init];
sourceLabel.text = recommendation.source;
sourceLabel.font = [UIFont systemFontOfSize:12];
sourceLabel.textColor = [UIColor secondaryLabelColor];
// Layout views...
// Configure viewability tracking — required for accurate impression reporting
[TeadsAdPlacementRecommendations configureViewabilityPerListingFor:view withRec:recommendation];
return view;
}
@end
Viewability Tracking
Viewability tracking is required for accurate impression reporting. Publishers who skip this step will see incorrect viewability metrics in their dashboard.
Call configureViewabilityPerListing(for:withRec:) for each recommendation view as you add it to your layout. Pass the view that displays the recommendation and its corresponding OBRecommendation:
- Swift
- Objective-C
// Call for each recommendation view once it is in the view hierarchy
TeadsAdPlacementRecommendations.configureViewabilityPerListing(
for: recommendationView,
withRec: recommendation
)
// Call for each recommendation view once it is in the view hierarchy
[TeadsAdPlacementRecommendations configureViewabilityPerListingFor:recommendationView
withRec:recommendation];
Once configured, the SDK tracks the view and automatically reports viewable impressions to Teads — no additional work is required.
AdChoices compliance
AdChoices compliance is required. Omitting the widget AdChoices icon or the per-item disclosure icon on paid recommendations violates Teads ad serving policies.
Widget AdChoices icon
Your recommendations widget must display a clickable AdChoices icon — a UIButton at least 30×30 pt, positioned above the widget and hidden until recommendations load. When tapped, open the URL returned by TeadsAdPlacementRecommendations.getAboutURL() in Safari or an SFSafariViewController:
- Swift
- Objective-C
import SafariServices
if let url = TeadsAdPlacementRecommendations.getAboutURL() {
let safari = SFSafariViewController(url: url)
present(safari, animated: true)
}
#import <SafariServices/SafariServices.h>
NSURL *url = [TeadsAdPlacementRecommendations getAboutURL];
if (url) {
SFSafariViewController *safari = [[SFSafariViewController alloc] initWithURL:url];
[self presentViewController:safari animated:YES completion:nil];
}
RTB disclosure icon (paid recommendations)
Paid (RTB) recommendations require a per-item disclosure icon overlaid on the thumbnail. Add a UIButton (minimum 30×30 pt) above each recommendation image, hidden by default.
For each recommendation, check shouldDisplayDisclosureIcon(). If it returns true, reveal the button, load its icon from disclosure.imageUrl, and open disclosure.clickUrl on tap; otherwise keep it hidden so it never shows on organic recommendations:
- Swift
- Objective-C
if recommendation.shouldDisplayDisclosureIcon() {
disclosureButton.isHidden = false
// Load recommendation.disclosure?.imageUrl and set it as the button image
// On tap, open recommendation.disclosure?.clickUrl in Safari or an SFSafariViewController
} else {
disclosureButton.isHidden = true
}
if ([recommendation shouldDisplayDisclosureIcon]) {
disclosureButton.hidden = NO;
// Load recommendation.disclosure.imageUrl and set it as the button image
// On tap, open recommendation.disclosure.clickUrl in Safari or an SFSafariViewController
} else {
disclosureButton.hidden = YES;
}
To verify your implementation, enable RTB test mode so paid recommendations always appear in responses:
- Swift
- Objective-C
TeadsAdPlacementRecommendations.testRTB = true
TeadsAdPlacementRecommendations.testRTB = YES;
Remember to remove this call before releasing to production.