Skip to content

Ads#

Introduction#

The Storyteller SDK supports displaying ads that can be created in the Storyteller CMS (First Party Ads), as well as Ads from Google Ad Manager, Google AdMob, VAST tags via SDK extensions developed by Storyteller, and Ads from other sources via custom implementation provided by the integrator.

Which source of ads is used can be configured on your behalf by a member of the Storyteller Delivery Team.

Storyteller First Party Ads#

If your tenant is configured to use Storyteller First Party Ads, which can be managed in the Storyteller CMS, then no changes to the Storyteller integration code are necessary. The Ads code is managed entirely within the Storyteller SDK.

Storyteller GAM SDK#

To use Ads from Google Ad Manager in Storyteller, first reach out to your Storyteller contact and they will assist you with setting up Google Ad Manager to traffic ads to Storyteller.

You will then need to use the Storyteller Google Ad Manager SDK extension to fetch the ads from Google Ad Manager.

To use this extension, first install it using Swift Package Manager or Cocoapods.

For Swift Package Manager, it is available on Github here:

https://github.com/getstoryteller/storyteller-gam-module-swift

For Cocoapods, first make sure to specify the sources for Cocoapods:

source 'https://github.com/getstoryteller/storyteller-sdk-ios-podspec.git'
source 'https://cdn.cocoapods.org/'

The StorytellerGAMIntegration is available by importing this pod:

pod 'StorytellerGAMIntegration'

Basic Setup#

Make sure to import the same version of StorytellerSDK and StorytellerGAMIntegration.

Now initialize the extension as follows:

import StorytellerSDK
import StorytellerGAMIntegration

let configuration = StorytellerGAMModuleConfiguration(
    adUnit: { requestInfo in
        return "YOUR_AD_UNIT_ID"
    }
)

You will need to supply the following parameter:

Parameter Name Description
adUnit A closure that returns the ID of the Ad unit in Google Ad Manager that will be used to serve the Storyteller Ads for the specific Ad request. This can be used for custom Ad units depending on the request context.
bottomBannerAdUnit Optional closure that returns the Ad unit ID used specifically for the Clips bottom banner placement. Leave this nil if you don't plan to serve Clips bottom banner Ads.

Then pass the newly created instance of the extension to the modules property on the Storyteller instance:

Storyteller.shared.modules = [StorytellerGAMModule(configuration: configuration)]

Our Showcase app uses this module to integrate ads - see the GAM module configuration in AppDelegate.setupStoryteller.

Setup with Dynamic Ad Unit Changes#

Example for dynamic Ad unit changes when you want to use different Ad units for Stories and Clips:

import StorytellerSDK
import StorytellerGAMIntegration

let configuration = StorytellerGAMModuleConfiguration(
    adUnit: { requestInfo in
        switch requestInfo {
        case .stories:
            return "/33813572/storyteller/stories"
        case .clips:
            return "/33813572/storyteller/clips"
        }
    }
)

Setup with Additional Parameters#

You can also supply optional parameters customNativeTemplateIds, publisherProvidedId, and customKvps if needed:

import StorytellerSDK
import StorytellerGAMIntegration

let configuration = StorytellerGAMModuleConfiguration(
    adUnit: { requestInfo in
        switch requestInfo {
        case .stories:
            return "/33813572/storyteller/stories"
        case .clips:
            return "/33813572/storyteller/clips"
        }
    },
    customNativeTemplateIds: StorytellerGAMModuleConfiguration.CustomNativeTemplateIds(
        stories: "YOUR_STORIES_TEMPLATE_ID",
        clips: "YOUR_CLIPS_TEMPLATE_ID"
    ),
    publisherProvidedId: {
        "YOUR_PUBLISHER_PROVIDED_ID"
    },
    customKvps: {
        [
            "YOUR_CUSTOM_KEY": "YOUR_CUSTOM_VALUE",
            "YOUR_PRIVACY_KEY": "YOUR_PRIVACY_VALUE"
        ]
    }
)
Parameter Name Description
customNativeTemplateIds If you have worked with the Storyteller Delivery team to setup Custom Native Ads, you will need to supply their IDs here. If you are only using Stories (but not Clips) it is only necessary to supply one property of this struct.
publisherProvidedId Optional closure that returns the Publisher Provided ID (PPID) for Google Ad Manager audience targeting. Return the identifier your GAM setup expects, or nil to omit PPID.
customKvps A closure that is called each time we request a new ad. The Storyteller GAM SDK passes a default set of KVPs to GAM to allow targeting based on the content of the Stories/Clips the user is viewing. If you have any additional parameters that you need to be able to target by, these should be passed here. Note that the SDK will not inherit any KVPs being set in the rest of your app. Do not pass PPID here; use publisherProvidedId for PPID.

Default KVPs#

The Storyteller GAM SDK automatically sends a set of key-value pairs (KVPs) to Google Ad Manager to enable content-based targeting. These KVPs are only sent when ad tracking is enabled (enableAdTracking == true in the StorytellerEventTrackingOptions you pass to Storyteller.shared.initialize(...)).

For Stories:

KVP Key Description
stCategories Categories associated with the current story
stCurrentCategory Categories of the list containing the story
stPlacement The placement identifier of the story
stApiKey The current Storyteller API key
stAdIndex The order of the ad within the story

For Clips:

KVP Key Description
stCollection The identifier of the clip collection
stClipCategories Categories associated with the current clip
stNextClipCategories Categories of the next clip (if available)
stApiKey The current Storyteller API key
stAdIndex The order of the ad within the clip collection

Storyteller VAST SDK#

The Storyteller VAST SDK extension requests vendor-neutral HTTPS VAST tags, resolves compatible inline and wrapper responses, selects playable linear media, and returns Storyteller-rendered fullscreen Ads through the standard module system.

The module is generic VAST support, not an ITV-specific integration. ITV is the first validated production-shaped scenario for the iOS VAST module, but the same module can be configured for any compatible VAST server.

To use this extension, first install it using Swift Package Manager or Cocoapods.

For Swift Package Manager, it is available on Github here:

https://github.com/getstoryteller/storyteller-vast-module-swift

For Cocoapods, first make sure to specify the sources for Cocoapods:

source 'https://github.com/getstoryteller/storyteller-sdk-ios-podspec.git'
source 'https://cdn.cocoapods.org/'

The StorytellerVASTIntegration is available by importing this pod:

pod 'StorytellerVASTIntegration'

Basic Setup#

Make sure to import the same version of StorytellerSDK and StorytellerVASTIntegration.

import StorytellerSDK
import StorytellerVASTIntegration

let configuration = StorytellerVASTModuleConfiguration(
    baseUrl: "https://ads.example.com/vast",
    requestParameters: { _ in
        [
            "placement": "storyteller"
        ]
    },
    urlFormat: .queryString
)

Storyteller.shared.modules = [StorytellerVASTModule(configuration: configuration)]

You will need to supply the following parameters:

Parameter Name Description
baseUrl HTTPS base URL of the VAST tag endpoint.
requestParameters Closure called for each Ad request. Return the key-value parameters your VAST provider needs for the specific Stories or Clips request context.
urlFormat Optional serialization strategy. Use .pathSegment to append parameters as /key=value path segments, or .queryString to append them as query parameters. Defaults to .pathSegment.
diagnosticsHandler Optional closure that receives request, parse, wrapper, media-selection, mapping, completion, and failure diagnostics.

The generated VAST tag URL must use HTTPS and be no longer than 2,048 bytes. The module supports compatible VAST 2.x, 3.x, and 4.x linear video responses, including wrappers up to the module depth limit. Unsupported creative types such as VPAID JavaScript are ignored during media selection.

The VAST module supports fullscreen Story and Clip Ads. It does not serve Clips bottom banner Ads.

GAM VAST Setup#

If you use Google Ad Manager to serve VAST video tags, use StorytellerGAMVASTModule. This module is part of StorytellerVASTIntegration; it does not use the Google Mobile Ads SDK or IMA SDK. It builds a GAM VAST tag request and then uses the same Storyteller VAST request, parsing, tracking, and fullscreen Player flow as StorytellerVASTModule.

import StorytellerSDK
import StorytellerVASTIntegration

let configuration = StorytellerGAMVASTModuleConfiguration(
    adUnit: { requestInfo in
        switch requestInfo {
        case .stories:
            return "/33813572/storyteller/stories_vast"
        case .clips:
            return "/33813572/storyteller/clips_vast"
        }
    },
    descriptionUrl: { _ in
        "https://example.com/storyteller-video"
    },
    contentUrl: { _ in
        "https://example.com/storyteller-video"
    },
    customParams: { _ in
        [
            "sliide_content_category": "sports"
        ]
    }
)

Storyteller.shared.modules = [StorytellerGAMVASTModule(configuration: configuration)]

You will need to supply the following parameters:

Parameter Name Description
adUnit Required closure that returns the GAM Ad Unit path for the current Ad request. This becomes the GAM iu parameter.
descriptionUrl Required closure that returns the canonical HTTPS URL describing the video content or Player context. This becomes the GAM description_url parameter.
contentUrl Optional closure that returns the page or content URL to send as the GAM url parameter. Return nil to omit it.
customParams Optional closure that returns GAM custom targeting parameters. The SDK serializes these into GAM's cust_params value, so pass unencoded keys and values.
tagParameters Optional closure that returns extra top-level GAM VAST tag parameters. These are applied after the SDK-generated GAM parameters, so a matching key overrides the generated value.
diagnosticsHandler Optional closure that receives the same VAST diagnostics events as StorytellerVASTModule.

StorytellerGAMVASTModule generates a request to https://pubads.g.doubleclick.net/gampad/ads with query-string parameters. It supplies iu, output=vast, env=vp, gdfp_req=1, sz, correlator, description_url, optional url, vpa=auto, vpmute, optional cust_params, and the SDK-owned VAST bid parameters listed below. Use tagParameters for any additional GAM VAST tag parameters required by your ad server setup.

The SDK serializes sz as <width>x<height> in physical pixels, with a lowercase x and no spaces or px suffix. It derives this from the active fullscreen Player window or screen and omits sz only when it cannot resolve a reliable non-zero size. customParams are serialized inside cust_params; for example, ["sliide_content_category": "sports"] becomes sliide_content_category=sports inside the cust_params value before the final request URL is encoded.

GAM VAST is VAST-backed, not Google Mobile Ads or IMA-backed. The module sets adSource to .custom("vast"), so Google paid ad analytics events are not emitted for GAM VAST requests.

See VAST and GAM VAST Parameter Tables for the parameters the SDK sets internally, where client-provided values are applied, and which values can be overridden.

The GAM VAST module supports fullscreen Story and Clip Ads. It does not serve Clips bottom banner Ads.

VAST and GAM VAST Parameter Tables#

Use these tables to decide whether a value belongs in generic VAST requestParameters, GAM VAST customParams, or GAM VAST tagParameters.

Surface Client Parameter Path SDK Behavior Override Behavior
Generic VAST tag parameters StorytellerVASTModuleConfiguration.requestParameters The SDK builds the Sliide VAST bid parameters first, then merges the returned key-value pairs and serializes the final result using the configured urlFormat. It does not add GAM-specific parameters. Matching keys in requestParameters override SDK-set Sliide VAST bid parameters.
GAM VAST custom targeting StorytellerGAMVASTModuleConfiguration.customParams The SDK encodes the returned key-value pairs into GAM's cust_params value. Change individual custom targeting values in customParams, or replace the generated cust_params by returning cust_params from tagParameters.
GAM VAST top-level parameters StorytellerGAMVASTModuleConfiguration.tagParameters The SDK builds GAM VAST parameters and Sliide VAST bid parameters first, then applies these top-level parameters. Matching keys in tagParameters are the final override layer.
Sliide VAST Bid Parameter Applies To Value / Source Static or Dynamic Client Override
adtype Generic VAST and GAM VAST 13 Static Generic: requestParameters["adtype"]; GAM VAST: tagParameters["adtype"]
plcmt Generic VAST and GAM VAST 3 Static Generic: requestParameters["plcmt"]; GAM VAST: tagParameters["plcmt"]
vw Generic VAST and GAM VAST Player width in physical pixels, for example 1080 Dynamic; omitted when no reliable size is available Generic: requestParameters["vw"]; GAM VAST: tagParameters["vw"]
vh Generic VAST and GAM VAST Player height in physical pixels, for example 1920 Dynamic; omitted when no reliable size is available Generic: requestParameters["vh"]; GAM VAST: tagParameters["vh"]
vminl Generic VAST and GAM VAST 5 Static Generic: requestParameters["vminl"]; GAM VAST: tagParameters["vminl"]
vmaxl Generic VAST and GAM VAST 30 Static Generic: requestParameters["vmaxl"]; GAM VAST: tagParameters["vmaxl"]
vfmt Generic VAST and GAM VAST 1 for MP4 playable media format on iOS Static Generic: requestParameters["vfmt"]; GAM VAST: tagParameters["vfmt"]
vadFmt Generic VAST and GAM VAST 2+3+8 Static Generic: requestParameters["vadFmt"]; GAM VAST: tagParameters["vadFmt"]
vplay Generic VAST and GAM VAST 6 for muted autoplay, 5 for sound-on autoplay Dynamic; falls back to 6 when mute state is unavailable Generic: requestParameters["vplay"]; GAM VAST: tagParameters["vplay"]
vskip Generic VAST and GAM VAST 1 Static Generic: requestParameters["vskip"]; GAM VAST: tagParameters["vskip"]
vpos Generic VAST and GAM VAST 0 Static Generic: requestParameters["vpos"]; GAM VAST: tagParameters["vpos"]
vcom Generic VAST and GAM VAST 0 Static Generic: requestParameters["vcom"]; GAM VAST: tagParameters["vcom"]
vcont Generic VAST and GAM VAST 1 Static Generic: requestParameters["vcont"]; GAM VAST: tagParameters["vcont"]
vtype Generic VAST and GAM VAST 1 Static Generic: requestParameters["vtype"]; GAM VAST: tagParameters["vtype"]
vminbtr Generic VAST and GAM VAST 600 Static Generic: requestParameters["vminbtr"]; GAM VAST: tagParameters["vminbtr"]
vmaxbtr Generic VAST and GAM VAST 8000 Static Generic: requestParameters["vmaxbtr"]; GAM VAST: tagParameters["vmaxbtr"]

vw and vh are separate integer values in physical pixels. Do not include px, point units, spaces, or an x separator in those values. For a fullscreen player measured as 1080 by 1920 physical pixels, the SDK sends vw=1080 and vh=1920.

Pure generic VAST does not use GAM cust_params. If you need GAM custom targeting such as a content category, pass it through GAM VAST customParams so the SDK encodes it into cust_params.

GAM VAST Top-Level Parameter Set By Default Default Source Value Type Client Override
output Yes SDK sets vast. Static tagParameters["output"]
env Yes SDK sets vp. Static tagParameters["env"]
gdfp_req Yes SDK sets 1. Static tagParameters["gdfp_req"]
iu Yes adUnit closure. Dynamic per Ad request tagParameters["iu"]
sz When a reliable non-zero size is available. SDK derives active fullscreen Player window or screen physical pixels as <width>x<height>. Dynamic per request context tagParameters["sz"]
correlator Yes SDK generates a fresh value for each request. Dynamic per Ad request tagParameters["correlator"]
description_url Yes descriptionUrl closure. Dynamic per Ad request tagParameters["description_url"]
url When non-nil contentUrl closure. Dynamic per Ad request tagParameters["url"]
vpa Yes SDK sets auto. Static tagParameters["vpa"]
vpmute Yes SDK sets 1 when the Player is muted and 0 when unmuted. Dynamic per request context tagParameters["vpmute"]
cust_params When customParams returns at least one key-value pair. Encoded customParams output. Dynamic per Ad request tagParameters["cust_params"]
GAM VAST Custom Parameter Recommended Client Path Encoded Location Notes
sliide_content_category customParams["sliide_content_category"] Inside cust_params. Use this for Sliide content category targeting.
Additional custom KVPs customParams Inside cust_params. Pass unencoded keys and values; the SDK handles GAM cust_params encoding.

VAST URL Formats#

Use .pathSegment when your VAST server expects parameters appended as path segments:

let configuration = StorytellerVASTModuleConfiguration(
    baseUrl: "https://ads.example.com/vast",
    requestParameters: { _ in
        [
            "placement": "stories",
            "adIndex": "1"
        ]
    },
    urlFormat: .pathSegment
)

This produces a request shaped like:

https://ads.example.com/vast/placement=stories/adIndex=1

Use .queryString when your VAST server expects standard query parameters:

let configuration = StorytellerVASTModuleConfiguration(
    baseUrl: "https://ads.example.com/vast",
    requestParameters: { _ in
        [
            "placement": "stories",
            "adIndex": "1"
        ]
    },
    urlFormat: .queryString
)

This produces a request shaped like:

https://ads.example.com/vast?placement=stories&adIndex=1

The SDK encodes parameter keys and values for the selected format. Pass unencoded values from your callback.

VAST Support in the First Release#

The first iOS release supports:

  • Inline linear fullscreen video Ads for Stories and Clips
  • Compatible VAST 2.x, 3.x, and 4.x parsing
  • Wrapper resolution and fallback to the first playable Ad in the response
  • Media file selection for compatible video media
  • Click-through and click tracking
  • Impression, creative view, start, quartile, complete, pause, resume, mute, unmute, close, and skip tracking
  • Multiple tracking URLs for the same VAST event
  • Deferred VAST error reporting when request, parse, wrapper, media selection, or mapping fails
  • Storyteller presentation metadata through Extension type="storyteller:ad-ui"
  • One compatible static-resource VAST <Icon> overlay for fullscreen Story and Clip Ads

The first iOS release does not support:

  • Nonlinear Ads
  • Companion Ads
  • OMID / verification rendering
  • VPAID or SIMID
  • Server-side ad insertion (SSAI)
  • Clips bottom banner Ads through VAST
  • VAST ad caption rendering
  • Rendering VAST <IFrameResource> or <HTMLResource> icons

VAST Tracking and Skip Behavior#

When a VAST response contains multiple URLs for the same supported tracking event, the SDK preserves and fires all of them for the matching Storyteller playback event.

VAST skip tracking maps to the Storyteller skipped-Ad flow. VAST skipoffset is converted to an ad-specific non-skippable duration in the Storyteller Player. When skipoffset is present, it controls the countdown for that VAST Ad; when it is absent, the SDK uses the tenant's CMS-configured non-skippable Ads behavior.

VAST <Error> URLs are reported by the VAST module when the SDK cannot request, parse, resolve, select media for, or map a VAST Ad. These failures cause the module to fail safely so Storyteller can continue through the normal module fallback flow.

VAST Branded Presentation Metadata#

VAST ads can provide Storyteller presentation metadata through Extension type="storyteller:ad-ui". The SDK reads CtaText and AdvertiserName from the StorytellerAdUi child element:

<Extension type="storyteller:ad-ui">
  <StorytellerAdUi>
    <CtaText>Shop now</CtaText>
    <AdvertiserName>Example Brand</AdvertiserName>
  </StorytellerAdUi>
</Extension>

The SDK resolves the final presentation values with this priority:

Field Resolution priority
CTA text VAST extension CtaText -> backend default ads.presentationDefaults.vast.ctaText -> Learn more
Advertiser display name VAST extension AdvertiserName -> backend default ads.presentationDefaults.vast.advertiserName -> VAST <Advertiser> -> VAST <AdTitle>

CTA text is only shown when the VAST ad has a non-empty <ClickThrough> destination. VAST <Icon> elements are reserved for industry, privacy, or program overlays and are not used as brand logos.

VAST Icon Overlays#

For fullscreen Story and Clip VAST ads, the SDK renders one compatible static-resource <Icon> overlay per ad only when the icon has program="AdChoices". Non-AdChoices icons, <IFrameResource> icons, and <HTMLResource> icons are parsed for diagnostics but are not rendered.

The icon is rendered inside the video frame, including landscape videos that are aspect-fitted inside the Player. The SDK honors VAST xPosition values of left, right, or a numeric x-coordinate, and yPosition values of top, bottom, or a numeric y-coordinate. The overlay has a fixed 24pt height and preserves the static resource aspect ratio for its width. VAST offset and duration values control when the icon appears, IconViewTracking is fired once when it first becomes visible, and IconClickTracking is fired when the icon is tapped. If IconClickThrough is present, tapping the icon opens that destination externally.

Setup with Dynamic Parameters#

Use StorytellerAdRequestInfo to provide different parameters for Stories and Clips:

let configuration = StorytellerVASTModuleConfiguration(
    baseUrl: "https://ads.example.com/vast",
    requestParameters: { requestInfo in
        switch requestInfo {
        case let .stories(placement, categories, story, adIndex):
            return [
                "placement": placement,
                "categories": categories.joined(separator: ","),
                "storyCategories": story.categories.map(\.externalId).joined(separator: ","),
                "adIndex": "\(adIndex)"
            ]
        case let .clips(collection, clip, nextClip, adIndex):
            return [
                "collection": collection,
                "clipCategories": clip.categories.map(\.externalId).joined(separator: ","),
                "nextClipCategories": nextClip?.categories.map(\.externalId).joined(separator: ",") ?? "",
                "adIndex": "\(adIndex)"
            ]
        }
    },
    urlFormat: .queryString
)

Storyteller AdMob SDK#

AdMob support uses the same StorytellerGAMIntegration artifact but a different module entry point. Only one ads integration module can be used at a time.

Due to AdMob limitations, banner Ads cannot be served from the same Ad unit as native Ads. adUnit is always used for native Ads, while bannerAdUnit is used for fullscreen banner Ads.

  • Default (enableBannerAdPriority = false): the module tries to load a native Ad from adUnit first. If native loading fails and bannerAdUnit is configured, it falls back to a fullscreen banner Ad from bannerAdUnit.
  • Banner priority enabled (enableBannerAdPriority = true): if bannerAdUnit is configured, the module tries to load a fullscreen banner Ad first. If banner loading fails, it falls back to a native Ad from adUnit.

enableBannerAdPriority only affects this fullscreen fallback order. It has no effect when bannerAdUnit is nil, and it does not change Clips bottom banner behavior configured through bottomBannerAdUnit.

Basic Setup#

import StorytellerSDK
import StorytellerGAMIntegration

let configuration = StorytellerAdMobModuleConfiguration(
    adUnit: { requestInfo in
        return "YOUR_NATIVE_AD_UNIT_ID"
    },
    bannerAdUnit: { requestInfo in
        return "YOUR_BANNER_AD_UNIT_ID"
    },
    bottomBannerAdUnit: { requestInfo in
        return "YOUR_BOTTOM_BANNER_AD_UNIT_ID"
    }
)

Storyteller.shared.modules = [StorytellerAdMobModule(configuration: configuration)]
Parameter Name Description
adUnit Required closure that returns the native Ad unit ID.
bannerAdUnit Optional closure that returns a fullscreen banner Ad unit ID. If supplied, the module can use it as the banner fallback path or the banner-first path when enableBannerAdPriority is enabled.
bottomBannerAdUnit Optional closure that returns the Ad unit ID used specifically for the Clips bottom banner placement. Leave this nil if you don't plan to serve Clips bottom banner Ads.
customKvps Optional closure that returns custom key-value pairs to attach to AdMob requests for targeting.
enableBannerAdPriority Optional flag that changes the fullscreen Ad loading order to banner first, then native fallback. This flag only has an effect when bannerAdUnit is configured.

For a complete integration example, see our Showcase app code here.

Setup with Banner Priority Enabled#

import StorytellerSDK
import StorytellerGAMIntegration

let configuration = StorytellerAdMobModuleConfiguration(
    adUnit: { _ in
        "YOUR_NATIVE_AD_UNIT_ID"
    },
    bannerAdUnit: { _ in
        "YOUR_BANNER_AD_UNIT_ID"
    },
    enableBannerAdPriority: true
)

Storyteller.shared.modules = [StorytellerAdMobModule(configuration: configuration)]

Setup with Dynamic Ad Unit Changes#

import StorytellerSDK
import StorytellerGAMIntegration

let configuration = StorytellerAdMobModuleConfiguration(
    adUnit: { requestInfo in
        switch requestInfo {
        case .stories:
            return "YOUR_STORIES_NATIVE_AD_UNIT_ID"
        case .clips:
            return "YOUR_CLIPS_NATIVE_AD_UNIT_ID"
        }
    },
    bannerAdUnit: { requestInfo in
        switch requestInfo {
        case .stories:
            return "YOUR_STORIES_BANNER_AD_UNIT_ID"
        case .clips:
            return "YOUR_CLIPS_BANNER_AD_UNIT_ID"
        }
    },
    bottomBannerAdUnit: { requestInfo in
        switch requestInfo {
        case .stories:
            return "YOUR_STORIES_BOTTOM_BANNER_AD_UNIT_ID"
        case .clips:
            return "YOUR_CLIPS_BOTTOM_BANNER_AD_UNIT_ID"
        }
    },
    customKvps: {
        ["appmode": "prod"]
    },
    enableBannerAdPriority: true
)

AdMob Test IDs for Local Validation#

The Showcase app uses the following sample AdMob unit IDs for local validation. Use your own production IDs outside test and debug flows.

Placement Sample AdMob unit ID
Native ca-app-pub-3940256099942544/3986624511
Native video ca-app-pub-3940256099942544/2521693316
Fullscreen banner fallback ca-app-pub-3940256099942544/2435281174
Clips bottom banner ca-app-pub-3940256099942544/2934735716

AdMob vs GAM at a Glance#

Concern GAM AdMob
Module entry point StorytellerGAMModule StorytellerAdMobModule
Native-specific options Supports customNativeTemplateIds for custom native Ads Uses standard native Ads and does not expose customNativeTemplateIds
Fullscreen banner setup No separate bannerAdUnit parameter Uses optional bannerAdUnit; this can be banner fallback or banner-first when enableBannerAdPriority is true
Shared options Supports bottomBannerAdUnit, publisherProvidedId, and customKvps Supports bottomBannerAdUnit, customKvps, and enableBannerAdPriority

Mutual Exclusivity#

StorytellerGAMModule and StorytellerAdMobModule are mutually exclusive. Configure only one of them at a time.

StorytellerVASTModule and StorytellerGAMVASTModule are separate fullscreen Ads modules. If you use either with any other Ads module, order the Storyteller.shared.modules array deliberately because the SDK asks modules for Ads in order and falls back to the next module when one throws.

Bottom Banner Ads#

The Clips Player supports bottom banner Ads rendered as standard banner views added to the hierarchy below the video view. Bottom banner Ads are disabled when StorytellerClipCollectionConfiguration.adConfiguration is omitted or set to nil, and for new StorytellerClipsAdConfiguration instances. To opt a Clips presentation into bottom banner Ads, pass StorytellerClipsAdConfiguration(bottomBannerEnabled: true) through StorytellerClipCollectionConfiguration.adConfiguration.

When using the GAM or AdMob module, supply bottomBannerAdUnit in the corresponding configuration to fetch bottom banner Ads. Opting in locally does not force Ads on: the tenant feed must still be configured to show bottom banner Ads, and the active Ads module must support this placement.

StorytellerVASTModule and StorytellerGAMVASTModule do not support bottom banner Ads.

Clips Opening Pre-Roll Ads#

If your tenant is configured for Clips Ads with initialIndex = 0, individual Clips presentations can opt into an opening fullscreen Ad before the first Clip is played by passing StorytellerClipsAdConfiguration(preRollEnabled: true) through StorytellerClipCollectionConfiguration.adConfiguration.

let configuration = StorytellerClipCollectionConfiguration(
    collectionId: "top-plays",
    adConfiguration: StorytellerClipsAdConfiguration(preRollEnabled: true)
)

Opening pre-roll is disabled when StorytellerClipCollectionConfiguration.adConfiguration is omitted or set to nil, and for new StorytellerClipsAdConfiguration instances. Opting in locally does not force Ads on: Clips Ads must still be available for the tenant, and the remote Clips Ad strategy must use initialIndex = 0. The request uses the first opened content Clip as the current Clip context and sends adIndex = 1, which maps to the default stAdIndex = 1 value for Google Ad Manager and AdMob integrations.

When a CMS-configured opening pre-roll timeout is reached before the Ad loads, the Player starts the content Clip and ignores any late opening pre-roll result. Later between-Clip Ads continue to use the normal Clips ad cadence and increment from the next Ad index.

Ad Request Information#

Note: This section is only relevant if you're implementing a custom Ads solution or building VAST request parameters. If you're using Storyteller First Party Ads, StorytellerGAMModule, or StorytellerAdMobModule, you don't need to work with this directly.

If your StorytellerModule (or StorytellerDelegate) provides integrating-app ads, set adSource to declare the source used by your implementation.

  • For custom ad implementations, use .custom("myNetwork").
  • For Google modules, use .gam for GAM and .admob for AdMob.
  • The VAST and GAM VAST modules set .custom("vast") automatically.
  • .storyteller is reserved for Storyteller First Party ads.

Setting adSource to .gam or .admob enables Google paid ad analytics events. See Ad Events.

StorytellerGAMModule, StorytellerAdMobModule, StorytellerVASTModule, and StorytellerGAMVASTModule set adSource automatically.

When implementing custom Ads, you'll receive context about the Ad request through the StorytellerAdRequestInfo enum. This provides information about what content the Ad will be displayed for.

StorytellerAdRequestInfo#

The StorytellerAdRequestInfo enum has two cases:

  • stories(placement: String, categories: [String], story: ItemInfo, adIndex: Int)

    Used when an Ad is requested for display in a Stories Player. The parameters include:

  • placement - The placement identifier of the Story

  • categories - An array of categories associated with the List that the Story is part of
  • story - An ItemInfo struct containing more information about the specific Story
  • adIndex - The order of the ad within the current playback session (starts from 1)

  • clips(collection: String, clip: ItemInfo, nextClip: ItemInfo?, adIndex: Int)

    Used when an Ad is requested for display in a Clips Player. The parameters include:

  • collection - The identifier of the Clip collection

  • clip - An ItemInfo struct containing detailed information about the current Clip
  • nextClip - An optional ItemInfo struct containing more information about the Clip that is to appear after the requested Ad
  • adIndex - The order of the Ad within the displayed Ads in a Clip collection (1 for the first Ad, 2 for the second, etc.)

ItemInfo#

Each case includes an ItemInfo struct that contains:

  • categories - An array of StorytellerCategory objects representing categories that the Story or Clip is part of.

Non Skippable Ads#

Our Player can enforce a period of time during which ads can't be skipped. When enabled, user interactions that would skip a Story or Clip Ad won't be allowed for that duration. This feature can be configured in the CMS.