Common Integration Instructions
Introduction
SFWidget is a new solution provided by Outbrain to integrate our Web-based solution for SmartLogic\Smartfeed in a native iOS app. This solution should work in either a ScrollView, UICollectionView or UITableView.
The general concept for integrating Outbrain Web-based solution in a native app – is to include SFWidget
which encapsulate WKWebView
which in turn loads the SmartLogic feed in a Web format with a native bridge to pass messages from\to native code.
SFWidget
is a sub-class of UIView
so app developers can either create a new instance from code or via Storyboard with an Outlet variable.
Common Integration Instructions
Registering App Configuration
You will need to register your app’s Outbrain configuration once during the initialization of your app, before calling any other Outbrain method. You can do this by calling Outbrain’s initializeOutbrainWithPartnerKey
method. This method takes a single appKey
parameter, which is a string that contains the application key you’ve received from your Outbrain account manager.
Here is an example of how to call initializeOutbrainWithPartnerKey
:
Objective C
[Outbrain initializeOutbrainWithPartnerKey:@"MyPartnerKey"];
Swift
Outbrain.initializeOutbrain(withPartnerKey: "MyPartnerKey")
In Your ViewController
1) Hold a global variable for the SFWidget:
var sfWidget: SFWidget!
2) Your ViewController should implement SFWidgetDelegate
:
extension YourVC: SFWidgetDelegate {
func didChangeHeight(_ newHeight: CGFloat) {
// See implementation for UITableView \ UICollectionView \ UIScrollView (at the bottom of this section)
}
// Optional delegate method - should be used for navigating to articles within the app (instead of external browser)
func onOrganicRecClick(_ url: URL) {
// handle click on organic URL - probably some in-app navigation to the article.
}
// Optional delegate method - should be used if the publisher is interested in manually tracking the "widget rendered" event.
func widgetRendered(_ articleUrl: String, widgetId: String, widgetIndex: Int) {
print("App received widgetRendered event: \(articleUrl) \(widgetId) \(widgetIndex)")
}
// Default delegate method - should open the URL in an external browser
func onRecClick(url: URL) {
let safariVC = SFSafariViewController(url: url)
self.navigationController?.present(safariVC, animated: true, completion: nil)
}
}
Notice that onOrganicRecClick
is an optional method. Use this method to handle clicks on organic recommendations (for example, navigate in your app).
3) Configure the SFWidget
in viewDidLoad
:
Simple Configure()
self.sfWidget.configure(with: self,
url: "https://mobile-demo.outbrain.com",
widgetId: "MB_1",
installationKey: "NANOWDGT01") // also called "Partner Key" or "Installation Key"
Advanced Configure()
/*
* @param url - the current screen "article url"
* @param widgetId - the widget id
* @param widgetIndex - should be 0 by default.
* Only if there are 2 widgets on the same page, the 2nd widget should have idx=1
* @param installationKey - also called "Partner Key" or "Installation Key"
* @param userId - set this property to the value of ["Advertiser ID"](https://developer.apple.com/documentation/adsupport/asidentifiermanager/1614151-advertisingidentifier) only if the user approves the usage of "app tracking" (advertiser ID) or if you want to override Apple Advertiser Id value to some custom user-id (must be used with the user approval.)
* @param darkMode - false by default - set to "true" if feed should be rendered in "dark mode"
*/
self.obSmartfeedWidget.configure(with: self,
url: OBConf.baseURL,
widgetId: OBConf.widgetID,
widgetIndex: 1,
installationKey: OBConf.installationKey,
userId: "F22700D5-1D49-42CC-A183-F36765261112",
darkMode: true)
4) Override method scrollViewDidScroll
:
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
sfWidget.scrollViewDidScroll(scrollView: scrollView)
}
UITableView
SFWidgetDelegate
In your implementation for SFWidgetDelegate
:
func didChangeHeight(_ newHeight: CGFloat)
tableView.beginUpdates()
tableView.endUpdates()
}
In viewDidLoad
Register SFWidgetTableCell
:
tableView.register(SFWidgetTableCell.self, forCellReuseIdentifier: "SFWidgetCell")
UITableView methods
numberOfRowsInSection:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return originalArticleItemsCount + 1
}
cellForRowAt indexPath:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell:UITableViewCell?
switch indexPath.row {
case 0:
cell = self.tableView.dequeueReusableCell(withIdentifier: imageHeaderCellReuseIdentifier) as UITableViewCell?
case 1:
cell = self.tableView.dequeueReusableCell(withIdentifier: textHeaderCellReuseIdentifier) as UITableViewCell?
case 2,3,4:
cell = self.tableView.dequeueReusableCell(withIdentifier: contentCellReuseIdentifier) as UITableViewCell?
default:
if let sfWidgetCell = self.tableView.dequeueReusableCell(withIdentifier: "SFWidgetCell") as? SFWidgetTableViewCell {
cell = sfWidgetCell
}
break;
}
return cell ?? UITableViewCell()
}
willDisplayCell for SFWidgetTableViewCell
:
This method will remove all “sub-views” from cell.contentView and then will this SFWidget as a sub-view with constraints to “fill parent”.
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
switch indexPath.row {
case 0:
return
case 1:
....
case 2,3,4:
....
default:
if let sfWidgetCell = cell as? SFWidgetTableCell {
self.sfWidget.willDisplay(sfWidgetCell)
}
break
}
}
heightForRowAt indexPath:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
switch indexPath.row {
case 0:
return UIDevice.current.userInterfaceIdiom == .pad ? 400 : 250;
case 1:
return UIDevice.current.userInterfaceIdiom == .pad ? 150 : UITableView.automaticDimension;
case 2, 3, 4:
return UIDevice.current.userInterfaceIdiom == .pad ? 200 : UITableView.automaticDimension;
default:
return self.sfWidget.getCurrentHeight();
}
}
UICollectionView
SFWidgetDelegate
In your implementation for SFWidgetDelegate
:
func didChangeHeight(_ newHeight: CGFloat)
collectionView.performBatchUpdates(nil, completion: nil)
}
In viewDidLoad
Register SFWidgetCollectionCell
:
collectionView.register(SFWidgetCollectionCell.self, forCellWithReuseIdentifier: "SFWidgetCell")
UICollectionView methods
numberOfItemsInSection:
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return originalArticleItemsCount + 1
}
cellForItemAt indexPath, cell for last item should be:
if let sfWidgetCell = collectionView.dequeueReusableCell(withReuseIdentifier: "SFWidgetCell", for: indexPath) as? SFWidgetCollectionViewCell {
return sfWidgetCell
}
willDisplayCell for SFWidgetCollectionViewCell
:
if let sfWidgetCell = cell as? SFWidgetCollectionViewCell {
sfWidget.willDisplaySFWidgetCell(cell: sfWidgetCell)
}
sizeForItemAt indexPath, for the last item (SFWidget):
return CGSize(width: collectionView.frame.size.width, height: self.sfWidget.getCurrentHeight())
UIScrollView
1) Embed SFWidget
inside a ScrollView. Make sure you create an Outlet to the SFWidget height constraints (see sample app for code example), for example:
@IBOutlet weak var sfWidgetHeightConstraint: NSLayoutConstraint!
2) In viewDidLoad()
you should init SFWidget
as explained in “Common Integration”.
3) In your implementation for SFWidgetDelegate
:
func didChangeHeight(_ newHeight: CGFloat)
self.sfWidgetHeightConstraint.constant = newHeight
}
SwiftUI Integration
Support Orientation Changes
You should override viewWillTransition
method.
For UITableView
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
sfWidget.viewWillTransition(coordinator: coordinator)
}
For UICollectionView
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
collectionView.collectionViewLayout.invalidateLayout()
sfWidget.viewWillTransition(coordinator: coordinator)
}
For UIScrollView
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
self.sfWidget.viewWillTransition(coordinator: coordinator)
coordinator.animate(alongsideTransition: nil) { _ in
self.scrollView.contentSize = CGSize(width: self.scrollView.frame.size.width, height: self.contentView.frame.size.height)
}
}