Introduction Download SDK and Sample App Integrating with the SDK Setup Smartfeed UICollectionView IntegrationStep 1: Create SmartFeedManagerStep 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


Please Note The Smartfeed Widget is currently deprecated, we strongly advise against using it since Outbrain will sunset this feature by Q1, 2023. Please follow the instructions for implementing the “Outbrain SDK Bridge (WebView)” solution which is Outbrain current recommended solution for App developers.

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.


Important: Please make sure to cover iOS 14 Changes, specifically the “SKAdNetwork support” and “App Install” part.


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.


Important: The `SmartFeedManager` should be a “property” in the UIViewController since the instance has to be alive while the UIViewController is alive (in other words, if `SmartFeedManager` will be a local variable it will be deallocated while the screen is still active).


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.


Please note: as the sample code above shows, it is recommended to set the SmartFeedManager `delegate` property in order to handle clicks on recommendations in the feed (and clicks on “Ad Choices” icon in case of recommendation of type OPA. It is also possible to not set the `delegate` property and let the SDK default delegate do the work for you. Read more about Smartfeed Delegate.


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


Important: SmartFeedManager method `willDisplay` must be called for every IndexPath in the feed, not only the ones in outbrainSectionIndex. This helps `SmartFeedManager` to know which cells are currently viewed by the user and when to load more items into the Smartfeed.


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

Please note: By default, Smartfeed just works, “out of the box”, by implementing steps 1-2 Smartfeed will be available in the Activity you set it on. Nevertheless, if you’d like to customize the appearance of the “Smartfeed View” in the RecycleView, you can definitely do it.

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.


Please note: It is recommended for the app developer to implement the `SmartFeedDelegate` protocol and set the `SmartFeedManager` `delegate` property in order to handle clicks on recommendations, Outbrain label and RTB icon in the feed.


@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


Important: The support for Smartfeed in the middle of UICollectionView is available in SDK >= v3.7.5

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:


Please note: it is highly recommended to take example from “Smartfeed” sample app. In file `ViewController.swift`, in method `viewDidLoad` make sure to set `performSegue(withIdentifier: “showCollectionMidPageVC“, sender: nil)`. Take example from ArticleMidPageCollectionViewController.


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.