Server-Guided Ad Insertion (SGAI)
HLS Interstitials on iOS, tvOS, and visionOS
The Bitmovin Player iOS SDK supports Server-Guided Ad Insertion (SGAI) through HLS interstitials out of the box. When an HLS source contains valid interstitial EXT-X-DATERANGE tags, the SDK parses them and schedules the resulting ad breaks automatically.
Interstitial playback does not require a separate ad schedule in the player configuration. Configure PlayerConfig.interstitialsConfig only when you need custom tracking, click-through mapping, or tracking macro replacement.
For cross-platform context, see Server-Guided Ad Insertion (SGAI).
Manifest Requirements
Each interstitial must be declared with CLASS="com.apple.hls.interstitial" and exactly one asset reference.
Use X-ASSET-URI for a single interstitial asset:
#EXT-X-DATERANGE:ID="midroll-1",CLASS="com.apple.hls.interstitial",START-DATE="2026-07-01T12:00:15Z",DURATION=10.0,X-ASSET-URI="https://example.com/ads/ad-1.m3u8",CUE="ONCE"Use X-ASSET-LIST when the ad opportunity contains multiple assets or asset-level metadata:
#EXT-X-DATERANGE:ID="midroll-2",CLASS="com.apple.hls.interstitial",START-DATE="2026-07-01T12:01:00Z",DURATION=20.0,X-ASSET-LIST="https://example.com/ads/midroll-2.json",CUE="ONCE"The asset list URL should return a JSON document that describes the interstitial assets:
{
"ASSETS": [
{ "URI": "https://example.com/ads/ad-2-a.m3u8", "DURATION": 10 },
{ "URI": "https://example.com/ads/ad-2-b.m3u8", "DURATION": 10 }
]
}Basic Setup
let sourceUrl = URL(string: "https://example.com/content-with-interstitials.m3u8")!
let sourceConfig = SourceConfig(url: sourceUrl, type: .hls)
player.load(sourceConfig: sourceConfig)
player.play()Tracking
To enable SVTA X-AD-CREATIVE-SIGNALING tracking, set the built-in mapping preset before creating the player:
let playerConfig = PlayerConfig()
playerConfig.interstitialsConfig.customAttributesMapping = InterstitialsConfig
.CustomAttributesMappingPresets
.adCreativeSignalingFor custom tracking metadata, provide your own mapping handler and register tracking URLs for the events you need:
playerConfig.interstitialsConfig.customAttributesMapping = { mappingData, mappingRegistry in
guard let trackingUrl = mappingData.customAttributes["X-IMPRESSION-URL"] as? String else {
return
}
mappingRegistry.tracking.register(
trackingEvent: InterstitialTrackingEvent(uris: [trackingUrl]),
for: .impression
)
}Use macroValueProvider when tracking URLs contain macros that must be resolved by the app:
playerConfig.interstitialsConfig.macroValueProvider = { macro in
switch macro.name {
case "USER_ID":
return ["current-user-id"]
default:
return macro.values
}
}Viewability Tracking
If viewability tracking URLs are provided through InterstitialsConfig, the SDK can fire .viewable, .notViewable, and .viewUndetermined tracking.
An interstitial is considered viewable after accumulating at least 2 seconds of playback while at least 50% of the ad view is visible. If that requirement is not met before the ad ends, .notViewable is emitted; if visibility cannot be measured, .viewUndetermined is emitted.
Observing Playback
Interstitials are exposed through the regular advertising event flow. Listen for events such as AdBreakStartedEvent, AdStartedEvent, AdQuartileEvent, AdFinishedEvent, AdSkippedEvent, AdBreakFinishedEvent, and AdErrorEvent.
Use the ads API and AdScheduleChangedEvent to inspect upcoming ad breaks and react to schedule changes. HLS interstitials from the manifest are scheduled by the SDK.
For HLS-specific ad-break metadata, cast the event ad break to InterstitialAdBreak. AdBreak.identifier contains the ID from the EXT-X-DATERANGE tag, and InterstitialAdBreak.plannedDuration exposes PLANNED-DURATION when present.
Dynamic Updates
For live workflows, an interstitial can be announced first and completed later through an update for the same ID. One common use case is adding X-PLAYOUT-LIMIT late to cut off the remaining ad-pod playback on demand and resume the main content.
#EXT-X-DATERANGE:ID="live-midroll-1",CLASS="com.apple.hls.interstitial",START-DATE="2026-07-01T12:02:00Z",DURATION=30.0,X-ASSET-LIST="https://example.com/ads/live-midroll-1.json"
# Later playlist update with the same ID, adding X-PLAYOUT-LIMIT
#EXT-X-DATERANGE:ID="live-midroll-1",CLASS="com.apple.hls.interstitial",START-DATE="2026-07-01T12:02:00Z",DURATION=30.0,X-ASSET-LIST="https://example.com/ads/live-midroll-1.json",X-PLAYOUT-LIMIT=15Dynamic updates are supported for X-RESUME-OFFSET, X-RESTRICT, and X-PLAYOUT-LIMIT. Updates add missing values only. Existing interstitial values are not overwritten or removed.
Repeated Playback and Reloading
HLS interstitials are re-playable by default. When the same interstitial opportunity is reached again, the SDK loads it again from the original URLs. For X-ASSET-LIST, this includes fetching the asset-list JSON again before loading the referenced media.
This allows ad decisioning services to return refreshed assets for repeated playback opportunities, for example when a viewer seeks back over an interstitial.
Use CUE="ONCE" when an interstitial should only play once.
Seeking and Ad Filtering
When playback seeks over interstitial positions, the SDK applies AdvertisingConfig.shouldPlaySeekedOverAdItems.
By default, seeked-over HLS interstitials without X-RESTRICT="JUMP" are not played after the seek. If one or more seeked-over interstitials are jump-restricted, the SDK plays the last jump-restricted interstitial.
Override shouldPlaySeekedOverAdItems only when your app needs different seek-over behavior.
Error Handling
Invalid interstitial manifest entries, such as missing or conflicting asset references, invalid URLs, or unsupported interstitial shape, are ignored and logged.
Failures during asset-list loading or decoding, empty asset lists, and ad media load or playback failures are reported through AdErrorEvent. Use AdManifestLoadEvent, AdManifestLoadedEvent, and AdErrorEvent to troubleshoot interstitial loading.
For networking customization and troubleshooting, X-ASSET-LIST requests are identified as HttpRequestType.hlsInterstitialsAssetList in the SDK networking APIs and DownloadFinishedEvent. See Network API for more context.
Limitations
- Use
AdScheduleChangedEventfor reliable schedule observation; HLS interstitial ad breaks may not be available immediately afterSourceLoadedEvent - Dynamic updates add missing supported values only; they do not overwrite or remove existing values
- Asynchronous
$remoteresolution from the SVTA ad creative signaling specification is not supported X-SNAP,X-TIMELINE-OCCUPIES,X-TIMELINE-STYLE, andX-CONTENT-MAY-VARYare not supported