iOS Bridge (SFWidget)
Intro
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.
Integration instructions
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.
Please make sure to follow the Getting Started guidelines before moving foreward.
Common Instructions
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
SDK version later than 5.2.0 support automatic viewability collection so this step is not needed. The scrollViewDidScroll
method is deprecated since v5.2.0.x
Make sure to call Outbrain SDK inside scrollViewDidScroll
, see example:
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
sfWidget.scrollViewDidScroll(scrollView: scrollView)
}
UITableView
SFWidgetDelegate - didChangeHeight
In your implementation for SFWidgetDelegate
add the code below to didChangeHeight
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
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();
}
}