Integrating Feed Ads in iOS

This guide walks you through all necessary steps to integrate feed ads into your iOS app.

If you have not done so already, please start with the Getting Started guide before reading on.


Integration

Overview

Follow these steps to integrate feed ads into your app:

  1. Prepare your UI
  2. Prepare your UIViewController
  3. Set Up and Load a Feed Ad
  4. Implement FAFeedAdDelegate
  5. Implement resizing

Depending on the complexity of your UI and the behavior you want to achieve, this will take you between 30 and 60 minutes.

1. Prepare your UI

Choose a place in your UI, where you would like to integrate feed ads. You need a container view to hold a feed ad. You could place it in a UICollectionView, a UITableView or even a static view somewhere in your UI.

Using a simple UIView

Create your container view, and prepare its UI to hold your feed ad.

Using a UICollectionView

Create a subclass of UICollectionView, and prepare its UI to hold your feed ad.

Using a UITableView

Create a subclass of UITableView, and prepare its UI to hold your feed ad.

2. Prepare your UIViewController

Create a property to hold your instance of FAFeedAd in your view controller. Make sure it is cleaned up when the view controller disappears.

Using Objective-C:

@interface FeedAdViewController ()

@property (nonatomic, strong) FAFeedAd *feedAd;

@end


@implementation FeedAdViewController

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];

    [self.feedAd cancel];
    [self.feedAd.adView removeFromSuperview];
    self.feedAd = nil;
}

@end

Using Swift:

class FeedAdViewController: UIViewController {
    var feedAd: FAFeedAd? = nil

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)

        feedAd?.cancel()
        feedAd?.adView.removeFromSuperview()
        feedAd = nil
    }
}

3. Set Up and Load a Feed Ad

Set up a feed ad by intializing it with a placement id and setting its delegate. Finally, call load to have the SDK load an ad.

Placement ID?

You can choose placement IDs yourself. A placement ID should be named after the ad position inside your product. For example, if you want to display an ad inside a list of news articles, you could name it "ad-news-overview".
A placement ID may consist of lowercase a-z, 0-9, - and _. You do not have to manually create the placement IDs before using them. Just specify them within the code, and they will appear in the FeedAd admin panel after the first request. Learn more about Placement IDs and how they are grouped to play the same Creative

Using Objective-C:

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    // Set up your FAFeedAd instance
    self.feedAd          = [[FAFeedAd alloc] initWithPlacementId:@"your-placement-id"];
    self.feedAd.delegate = self;

    // Load ad
    [self.feedAd load];
}

Using Swift:

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    // Set up your FAFeedAd instance
    feedAd = FAFeedAd(placementId: "your-placement-id")
    feedAd?.delegate = self
    feedAd?.load()
}

4. Implement FAFeedAdDelegate

Declare conformance to the protocol FAFeedAdDelegate and implement its methods in your view controller.

Feed ads start playing as soon as the ad view of an FAFeedAd becomes visible in your UI. Add it to your container view, when the method feedAdDidFinishLoading: gets called.

If a feed ad has no more ads to display, feedAdDidFinishPlaying:, will be called. If an error occurs feedAd:didFailToLoadWithError: or feedAd:didFailToPlayWithError: will be called. In all three cases, remove the ad view of FAFeedAd from your UI.

Ads within lists

FeedAds are meant to be displayed within scrollable content. Try to hold content for your UICollectionView or UITableView inside a (multi-dimensional) array that maps to your sections and rows / items to be displayed. Your implementations of UICollectionViewDelegate or UITableViewDelegate should then fetch content from that array to set up the correct cell for the type of data at hand.

Injecting ads in-between your content is then merely a task of injecting your FAFeedAd instance into your content array using a for loop. The code for injecting ads can be easily shared between instances of UIViewController by embedding it inside different classes based on the strategy pattern.

In the following code examples, this injection is resembled by the method injectFeedAd:into:.

Using a simple UIView

Using Objective-C:

- (void)feedAdDidFinishLoading:(FAFeedAd *)feedAd {
    // Add ad view to container view
    UIView *adView = self.feedAd.adView;
    adView.frame = self.adContainerView.bounds;
    [self.adContainerView addSubview:adView];
}

Using Swift:

func feedAdDidFinishLoading(_ feedAd: FAFeedAd!) {
    // Add ad view to container view
    if let adView = feedAd.adView {
      adView.frame = adContainerView.bounds
      adContainerView.addSubview(adView)
    }
}

Using a UICollectionView

Using Objective-C:

- (void)feedAdDidFinishLoading:(FAFeedAd *)feedAd {
    // Inject the feed ad into a copy of your content data
    self.displayedData = [self injectFeedAd:self.feedAd into:self.data];

    [self.collectionView reloadData];
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    id data = [self.displayedData objectAtIndex:indexPath.row];

    if ([data isKindOfClass:[FAFeedAd class]]) {
        FAFeedAd *feedAd = (FAFeedAd *) data;

        FAFeedAdCollectionViewCell *cell = [self.collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass([FAFeedAdCollectionViewCell class]) forIndexPath:indexPath];
        [cell injectAdView:feedAd.adView];
        return cell;
    }

    // ...

    return nil;
}

Using Swift:

func feedAdDidFinishLoading(_ feedAd: FAFeedAd!) {
    // Inject the feed ad into a copy of your content data
    displayData = injectFeedAd(feedAd, into: data)

    collectionView.reloadData()
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let currentElement = displayData[indexPath.row]

    if let ad = currentElement as? FAFeedAd {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: FeedAdCollectionViewCell.self), for: indexPath) as! FeedAdCollectionViewCell
        cell.injectAdView(ad.adView)
        return cell
    }

    // ...

    return cell
}

Using a UITableView

Using Objective-C:

- (void)feedAdDidFinishLoading:(FAFeedAd *)feedAd {
    // Inject the feed ad into a copy of your content data
    self.displayedData = [self injectFeedAd:self.feedAd into:self.data];

    [self.tableView reloadData];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    id data = [self.displayedData objectAtIndex:indexPath.row];

    if ([data isKindOfClass:[FAFeedAd class]]) {
        FAFeedAd *feedAd = (FAFeedAd *) data;

        FAFeedAdTableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:NSStringFromClass([FAFeedAdTableViewCell class])];
        [cell injectAdView:feedAd.adView];
        return cell;
    }

    // ...

    return nil;
}

Using Swift:

func feedAdDidFinishLoading(_ feedAd: FAFeedAd!) {
    // Inject the feed ad into a copy of your content data
    displayData = injectFeedAd(feedAd, into: data)

    tableView.reloadData()
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let currentElement = displayData[indexPath.row]

    if let ad = currentElement as? FAFeedAd {
        let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: FeedAdTableViewCell.self), for: indexPath) as! FeedAdTableViewCell
        cell.injectAdView(ad.adView)
        return cell
    }

    // ...

    return cell
}

5. Implement Resizing

To guarantee a seamless integration into your UI and to prevent distractions by letterboxing, you should resize your container views in your UI to match the preferred size of a feed ad.

The ad views of an FAFeedAd are set up to adjust their sizes following the desired size, aspect ratio and scalability of the displayed ad. The default aspect ratio is 16:9.

Every time an FAFeedAd changes its preferred size, the delegate method feedAdDidChangeSize: gets called. To determine the perfect size for your container views, use - (CGSize) [FAFeedAd sizeForSuperviewSize:].

The method sizeForSuperviewSize: takes a parameter of type CGSize, into which you should pass the size of your container view. If either width or height of your container view is flexible, set it to CGFLOAT_MAX.

For example, in the case of an iPhone app with a UITableView that runs over the full width of the screen: When you integrate a feed ad here, you will most likely be limited by the width of the screen. However, each cell can be flexible in height. In this case, pass the width of your UITableView and set the height to CFLOAT_MAX, to have sizeForSuperviewSize: determine the perfect height for your cell.

Using a simple UIView

Using Objective-C:

- (void)feedAdDidChangeSize:(FAFeedAd *) {
    // For a view which is only bounded by its width
    CGSize preferredSize = [feedAd sizeForSuperviewSize:CGSizeMake(self.adContainerView.frame.width, CGFLOAT_MAX)];

    self.adContainerView.frame = ({
        CGRect frame = self.adContainerView.frame;
        frame.size   = preferredSize;
        frame;
    });

    self.adContainerView.center = self.view.center;
}

Using Swift:

func feedAdDidChangeSize(_ feedAd: FAFeedAd!) {
    // For a view which is only bounded by its width
    let preferredSize = feedAd.size(forSuperview: CGSize(width: self.adContainerView.frame.width, height: CGFloat.greatestFiniteMagnitude))

    adContainerView.frame.size = preferredSize
    adContainerView.center = view.center
}

Using a UICollectionView

Using Objective-C:

- (void)feedAdDidChangeSize:(FAFeedAd *) {
    [self.collectionView.collectionViewLayout invalidateLayout];
}

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
    id data = [self.displayedData objectAtIndex:indexPath.row];

    if ([data isKindOfClass:[FAFeedAd class]]) {
        return [self.feedAd sizeForSuperviewSize:CGSizeMake(self.collectionView.frame.size.width, CGFLOAT_MAX)];
    }

    // ...

    return size;
}

Using Swift:

func feedAdDidChangeSize(_ feedAd: FAFeedAd!) {
    collectionView.collectionViewLayout.invalidateLayout()
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let currentElement = displayData[indexPath.item]

    if let ad = currentElement as? FAFeedAd {
        return ad.size(forSuperview: CGSize(width: collectionView.frame.size.width, height: CGFloat.greatestFiniteMagnitude))
    }

    // ...

    return size
}

Using a UITableView

Using Objective-C:

- (void)feedAdDidChangeSize:(FAFeedAd *) {
    [self.tableView beginUpdates];
    [self.tableView endUpdates];
}

- (CGFloat)tableView:(UITableView *) heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    id data = [self.displayData objectAtIndex:indexPath.row];

    if ([data isKindOfClass:[FAFeedAd class]]) {
        return [self.feedAd sizeForSuperviewSize:CGSizeMake(self.tableView.frame.size.width, CGFLOAT_MAX)].height;
    }

    // ...

    return height;
}

Using Swift:

func feedAdDidChangeSize(_ feedAd: FAFeedAd!) {
    tableView.beginUpdates()
    tableView.endUpdates()
}

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    let currentElement = displayData[indexPath.row]

    if let ad = currentElement as? FAFeedAd {
        return ad.size(forSuperview: CGSize(width: tableView.frame.size.width, height: CGFloat.greatestFiniteMagnitude)).height
    }

    // ...

    return height
}

It's a Wrap

Once you have completed all of the above steps, you should have a working feed ads integration.


FAFeedAd Delegate Methods

Implement the delegate methods from FAFeedAdDelegate to be notified about events and to customize its appearance:

  • an ad has been loaded successfully
- (void)feedAdDidFinishLoading:(FAFeedAd *)feedAd;
  • An ad has played through successfully
- (void)feedAdDidFinishPlaying:(FAFeedAd *)feedAd;
  • Errors occured during loading of an ad
- (void)feedAd:(FAFeedAd *)feedAd didFailToLoadWithError:(NSError *)error;
  • Errors occured during playback of an ad
- (void)feedAd:(FAFeedAd *)feedAd didFailToPlayWithError:(NSError *)error;
  • An ad has changed its desired size
- (void)feedAdDidChangeSize:(FAFeedAd *)feedAd;
  • An ad logged an impression
- (void)feedAdDidLogImpression:(FAFeedAd *)feedAd;
  • Should a loading indicator be displayed for an ad in the current context?
- (BOOL)feedAdShouldDisplayLoadingIndicator:(FAFeedAd *)feedAd;
  • An ad has been clicked
- (void)feedAdWasClicked:(FAFeedAd *)feedAd;
  • An ad has been skipped
- (void)feedAdWasSkipped:(FAFeedAd *)feedAd;
  • An ad will leave the application to show its landing page after the user has clicked the ad
- (void)feedAdWillLeaveApplication:(FAFeedAd *)FeedAd;

Advanced Options

Custom Parameters

You can set up an FAFeedAd with custom parameters to pass additional tracking data about the context in which the ad is played, e.g. the category of an item viewed by the user, using its dedicated initializer:

- (instancetype)initWithPlacementId:(NSString *)placementId customParameters:(NSDictionary<NSString *, NSString *> *)customParameters;

Continuous Ad Playback

Feed ads support continuous playback across screen changes, as described in FeedAd Behavior.

To benefit from this behavior for your app, make sure that your placements are linked to the same placement group and follow the integration instructions above for each of your screens.

Passing ads between screens, is then handled by the FeedAd SDK.


Using Feed Ads as Part of an Ad Mediation Waterfall

The FeedAd SDK has been designed with ad mediation waterfalls in mind.

We have implemented and tested FeedAd integration with major mediation networks. Read the mediation documentation to learn more about supported networks or take a look into our example app.

Whenever one of the following delegate methods from FAFeedAdDelegate has been called, the FAFeedAd instance finishes any outstanding tasks and then developers are in control again:

  • feedAdDidFinishLoading:
  • feedAdDidFinishPlaying:
  • feedAd:didFailToLoadWithError:
  • feedAd:didFailToPlayWithError:

This allows you to:

  • Simply make another call to load to have the SDK load a new ad or
  • Remove the ad view from screen, to deallocate the feed ad and to move on in your mediation

It will not automatically reload and try to display another ad.

Another important point in this regard, is that whenever an FAFeedAd detects that it does not have another ad to display, it will simply return a no-fill error instead of idling on your ad placement. This allows you to maximize ad revenues by moving on to the next step in your ad mediation waterfall.