Introduction
Download SDK and Sample App
Integrating with the SDK
Setup Smartfeed
UIStackView Integration
Step 1: Storyboard
Step 2: Set Up Constraints
Step 3: In Code – ViewDidLoad
Step 4: In Code – Setup SmartFeedManager
Step 5: In Code – Update UITableView Constraint
Step 6: In Code – Update UICollectionView Constraint
Step 7: In Code – Fetch More Recommendations
Smartfeed Delegate
Smartfeed RTB Support
Smartfeed Video Item


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, in this guide we’ll want to use UICollectionView.


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).


UIStackView Integration


Step 1: Storyboard

In order to get this solution to work, the view hierarchy structure is super important. Please notice the recommended way to structure the View Controller hierarchy:

- UIScrollView
  - UIStackView
    - UITableView (contains the article content)
    - UICollectionView (contains the Smartfeed content)


Step 2: Set Up Constraints

1) “Equals” constraint: UIStackView.width == UIScrollView.width
2) “Equals” constraint: UICollectionView.width == UIView.width (top-level view).
3) “Height” constraint: UITableView – give it some initial height (650 for example) and create an Outlet to be used in code later on.
4) “Height” constraint: UICollectionView – give it some initial height (650 for example) and create an Outlet to be used in code later on.


Step 3: In Code – ViewDidLoad

First we want to disable the scrolling on both UITableView and UICollectionView. We do that because in runtime we will update the “height” constraints we set in “step 2” according to the “content size” (example will follow).

We also want to set ourselves as the scrollview delegate.

override func viewDidLoad() {
    super.viewDidLoad()  

    scrollView.delegate = self
    tableView.isScrollEnabled = false
    sfCollectionView.isScrollEnabled = false
    setupSmartFeed()
    ...
    ...
    // Your code is here
}


Step 4: In Code – Setup SmartFeedManager

func setupSmartFeed() {
    self.smartFeedManager = SmartFeedManager(url: OBConf.baseURL, widgetID: OBConf.widgetID, collectionView: self.sfCollectionView)
    self.smartFeedManager.delegate = self
    self.smartFeedManager.isInMiddleOfScreen = true
    self.smartFeedManager.outbrainSectionIndex = 0
    self.smartFeedManager.useDefaultCollectionViewDelegate = true
    self.smartFeedManager.fetchMoreRecommendations() // start fetching manually because Smartfeed is in the middle
}


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.

More Notes:

1) We set outbrainSectionIndex since the CollectionView will contain only the Smartfeed.

2) useDefaultCollectionViewDelegate will set the SDK as the delegate of the CollectionView.


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 5: In Code – Update UITableView Constraint

We’ll need to update the height constraints of the UITableView by using the code below in willDisplay:cell:

if (indexPath.row == originalArticleItemsCount-1) {
    let height = self.tableView.contentSize.height
    print("set tableView height to: \(height)")
    self.tableViewHeight.constant = height
    self.view.setNeedsLayout()
}


Step 6: In Code – Update UICollectionView Constraint

We’ll need to update the height constraints of the UICollectionView by using the code below in smartfeedIsReadyWithRecs

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.sfCollectionView.reloadData()
    }
    let height = self.sfCollectionView.collectionViewLayout.collectionViewContentSize.height
    print("set collectionview height to: \(height)")
    self.sfCollectionViewHeight.constant = height
    self.view.setNeedsLayout()
}


Step 7: In Code – Fetch More Recommendations

It is the app developer responsibility to fetch more recommendations to the feed as the user scrolls down. Remember, the first load is happening in setupSmartFeed().

Here is a suggestion of how app developer may implement the “fetch more” functionality in code:

extension ArticleStackViewController : UIScrollViewDelegate {
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if (scrollView.contentOffset.y + 400 >= (scrollView.contentSize.height - scrollView.frame.size.height)) {
            if (self.smartFeedManager.hasMore) {
                if self.lastFetchMoreRecommendationFireTime + 0.4 < NSDate().timeIntervalSince1970 {
                    self.lastFetchMoreRecommendationFireTime = NSDate().timeIntervalSince1970
                    self.smartFeedManager.fetchMoreRecommendations()
                }
            }
        }
    }
}


(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 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).")
        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: