Introduction Download SDK and Sample App Integrating with the SDK Setup Smartfeed UICollectionView Integration – Step 1: Create SmartFeedManager – Step 2: Connect Your Data Source to SmartFeedManager Smartfeed Optional Properties Smartfeed Delegate Smartfeed RTB Support Smartfeed Video Item (Optional) Smartfeed In Middle of UITableView (Optional) Read More Button
Introduction
Outbrain Smartfeed is a brand new way for publishers to integrate Outbrain recommendations in their native app in a way that feels natural to their users (infinite scrolling at the end of the original article) and seamless for app developers to integrate.
See example of how Smartfeed works below:
Download SDK and Sample App
Download links are available at Outbrain SDK – Documentation & Download Links
Integrating with the SDK
Smartfeed is an integral part of the SDK, therefore you must integrate and init the SDK according to the instructions in our main developers guide page.
Setup Smartfeed
Finally we get to the interesting part of this guide, here are the actual steps necessary for app developers to make use of Smartfeed in their application.
SmartFeedManager
is the Class responsible for managing the Smartfeed. You should create an instance of SmartFeedManager in the relevant UIViewController where the original article is displayed.
The SmartFeedManager
constructor can work with both: UICollectionView
and UITableView
. The integration is pretty similar, please refer to the one relevant to you.
UICollectionView Integration
Step 1: Create SmartFeedManager
We recommend to setup SmartFeedManager
in viewDidLoad
method, for example:
override func viewDidLoad() {
super.viewDidLoad()
setupSmartFeed()
...
...
// Your code is here
}
func setupSmartFeed() {
guard let collectionView = self.collectionView else {
return
}
guard let publisherLogoImage = UIImage(named: "cnn-logo") else {
return
}
let baseURL = "https://mobile-demo.outbrain.com/2013/12/15/test-page-2"
self.smartFeedManager = SmartFeedManager(url: baseURL, widgetID: "SFD_MAIN_2", collectionView: collectionView)
self.smartFeedManager.delegate = self
// Optional
self.setupCustomUIForSmartFeed()
}
We initilize SmartFeedManager with:
1) baseURL – the current article URL
2) widgetID – an additional Outbrain widget ID — different from the primary widgetID (has to be configured to be of type “smartfeed”, please consult with you account manager for more details).
3) collectionView – the collection view instance.
We will explain about setupCustomUIForSmartFeed()
later on in this guide.
(Optional) set the widget index
By default the “widget index” for the Smartfeed is 0, however in case there are additional Outbrain widgets on the screen besides the Smartfeed, it’s possible to set the “widget index” via:
self.smartFeedManager.outbrainWidgetIndex = 2
Step 2: Connect Your Data Source to SmartFeedManager
SmartFeedManager should be called from key methods in the collection view data source.
The methods you should call SmartFeedManager
are:
1) func numberOfSections(in collectionView: UICollectionView) -> Int
2) func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
3) func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
4) func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath)
5) func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
6) func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat
numberOfSections
Smartfeed recommendations will be added into a separate section in the collection view to not interfere with the original data source implementation.
override func numberOfSections(in collectionView: UICollectionView) -> Int {
self.smartFeedManager.outbrainSectionIndex = 2 // update smartFeedManager with outbrain section index, must be the last one.
return self.smartFeedManager.numberOfSectionsInCollectionView()
}
numberOfItemsInSection
override func collectionView(_ collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
if section < self.smartFeedManager.outbrainSectionIndex {
return originalArticleItemsCount
}
else {
return self.smartFeedManager.smartFeedItemsCount()
}
}
cellForItemAt
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.section == self.smartFeedManager.outbrainSectionIndex {
return self.smartFeedManager.collectionView(collectionView, cellForItemAt: indexPath)
}
// Your app original implementation
}
willDisplay
override func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
self.smartFeedManager.collectionView(collectionView, willDisplay: cell, forItemAt: indexPath)
if indexPath.section == self.smartFeedManager.outbrainSectionIndex {
return;
}
// App Developer should configure the app cells here..
}
sizeForItemAt
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
if indexPath.section == self.smartFeedManager.outbrainSectionIndex {
return self.smartFeedManager.collectionView(collectionView, layout: collectionViewLayout, sizeForItemAt: indexPath)
}
// Your app original implementation
}
minimumLineSpacingForSectionAt
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
if section == self.smartFeedManager.outbrainSectionIndex {
return self.smartFeedManager.collectionView(collectionView, layout: collectionViewLayout, minimumLineSpacingForSectionAt: section)
}
return 5.0
}
(Optional): Implement Custom UI
For more information please refer to: iOS SDK – Smartfeed UICollectionView Custom UI
Smartfeed Optional Properties
smartFeedManager.disableCellShadows
Setting this property to “true” will disable shadows on each Smartfeed cell.
smartFeedManager.displaySourceOnOrganicRec
Setting this property to “true” will display the “source name” label on organic recs as well.
smartFeedManager.horizontalContainerMargin
Setting this property to value of 20.0 for example, will add margin on the “left” and “right” sides of the horizontal cell.
smartFeedManager.darkMode
Setting this property to “true” will change the Smartfeed colors scheme to “dark mode”
smartFeedManager.outbrainWidgetIndex
Set this value if there are more Outbrain widgets on the screen where the Smartfeed is displayed.
smartFeedManager.externalID
Setting this property will add extid
param to all ODB requests with the value of externalID param.
Smartfeed Delegate
Since SDK v3.7.0, app developers can now skip on the SmartFeedDelegate
implementation, in which case the SDK will handle all Smartfeed clicks by default implementation, i.e. open all links in SFSafariViewController
.
@protocol SmartFeedDelegate
-(void) userTappedOnRecommendation:(OBRecommendation *_Nonnull)rec;
-(void) userTappedOnAdChoicesIcon:(NSURL *_Nonnull)url;
-(void) userTappedOnVideoRec:(NSURL *_Nonnull)url;
-(void) userTappedOnOutbrainLabeling;
@end
An example for delegate implementation might be:
extension ArticleCollectionViewController : SmartFeedDelegate {
func userTapped(on rec: OBRecommendation) {
print("You tapped rec \(rec.content).")
if rec.isAppInstall {
print("rec tapped: \(rec.content) - is App Install");
Outbrain.openAppInstallRec(rec, inNavController: self.navigationController!)
return;
}
guard let url = Outbrain.getUrl(rec) else {
print("Error: no url for rec.")
return
}
let safariVC = SFSafariViewController(url: url)
self.navigationController?.present(safariVC, animated: true, completion: nil)
}
func userTapped(onAdChoicesIcon url: URL) {
print("You tapped onAdChoicesIcon")
let safariVC = SFSafariViewController(url: url)
self.navigationController?.present(safariVC, animated: true, completion: nil)
}
func userTapped(onVideoRec url: URL) {
print("You tapped on video rec")
let safariVC = SFSafariViewController(url: url)
self.navigationController?.present(safariVC, animated: true, completion: nil)
}
func userTappedOnOutbrainLabeling() {
print("You tapped on Outbrain Labeling")
let url = Outbrain.getAboutURL()
let safariVC = SFSafariViewController(url: url)
self.navigationController?.present(safariVC, animated: true, completion: nil)
}
}
Smartfeed RTB Support
SmartFeedManager
supports RTB “out of the box”, i.e. the displaying of the “Ad Choices” icon is done for you. All you, as the app developer, should do, is make sure to implement the click handling for the ad choices icon. On click you should open the provided URL in a browser. See example code below:
func userTapped(onAdChoicesIcon url: URL) {
print("You tapped onAdChoicesIcon")
let safariVC = SFSafariViewController(url: url)
self.navigationController?.present(safariVC, animated: true, completion: nil)
}
Smartfeed Video Item
OBSmartFeed
supports recommendations of type “in-widget” Video. Which is a video on top of existing “display recommendations” (see example below). OBSmartfeed will try to load a video rec if configured in the widget settings. If video will be available, it will be played on top of the widget it was configured on. When the video is finished or closed, the “display recommendations” will be show again.
See demo video below:
(Optional) Smartfeed In Middle of UICollectionView
Smartfeed can also be integrated in the middle of the screen so that additional non-Outbrain content can be placed below it. In order to achieve that, app developer should make the following changes.
App developer should make use of smartfeedIsReadyWithRecs()
method of SmartFeedDelegate
as shown below:
In Corresponding ViewController
self.smartFeedManager = SmartFeedManager(url: Const.baseURL, widgetID: Const.widgetID, collectionView: collectionView)
self.smartFeedManager.delegate = self
self.smartFeedManager.isInMiddleOfScreen = true
self.smartFeedManager.outbrainSectionIndex = 1 // update smartFeedManager with outbrain section index
self.smartFeedManager.fetchMoreRecommendations() // start fetching manually because Smartfeed is in the middle
In Corresponding SmartFeedDelegate
func smartfeedIsReadyWithRecs() {
if (!self.smartfeedIsReady) {
// only for the first time run reloadData() after that the Smartfeed will take care of updating itself
self.smartfeedIsReady = true
self.collectionView.reloadData()
}
}
In Corresponding Data Source
override func numberOfSections(in collectionView: UICollectionView) -> Int {
if self.smartfeedIsReady {
// numberOfSections including Smartfeed
return 3
}
else {
return 2
}
}
override func collectionView(_ collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
if self.smartfeedIsReady && section == self.smartFeedManager.outbrainSectionIndex {
return self.smartFeedManager.smartFeedItemsCount()
}
else {
return articleSectionItemsCount
}
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// create a new cell if needed or reuse an old one
var cell:UICollectionViewCell?
if self.smartfeedIsReady && indexPath.section == self.smartFeedManager.outbrainSectionIndex {
return self.smartFeedManager.collectionView(collectionView, cellForItemAt: indexPath)
}
// Continue with app implementation
}
override func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if self.smartfeedIsReady && indexPath.section == self.smartFeedManager.outbrainSectionIndex {
self.smartFeedManager.collectionView(collectionView, willDisplay: cell, forItemAt: indexPath)
if (self.smartFeedManager.hasMore && indexPath.row + 3 >= self.smartFeedManager.smartFeedItemsCount()) {
// manually fetch more recommendations when feed is near the end
self.smartFeedManager.fetchMoreRecommendations()
}
return
}
// App Developer should configure the app cells here..
}
(Optional) Read More Button
Read the instructions on this page to learn how to use the “Read More” functionality to increase RPM performance for your Smartfeed.