Paused Ads on tvOS
Let StreamLayer deliver a paused ad automatically on each ad break on tvOS — sourced from Studio or Prebid — by signalling pause/resume and selecting the delivery source.
Paused ads are StreamLayer-delivered ads shown automatically when the viewer pauses. Your app only signals the pause and resume; StreamLayer decides which ad to show and renders it. The ad is sourced either from Studio (server-driven inventory and rotation, the default) or from Prebid (an SDK-managed programmatic auction). This flow is tvOS-only.
Paused ads vs. exposed ads. With paused ads, your app signals when (an ad break) and StreamLayer chooses what (Studio or Prebid). With exposed ads, your app decides both — it calls
showAd(_:)/showVASTAd(_:vastURL:)on demand and supplies the creative. Use paused ads when you want StreamLayer to manage delivery; use exposed ads when you want to drive the content yourself.
Prerequisites
- Complete the tvOS Integration Guide.
- Create an event session and the StreamLayer Element:
StreamLayer.createSession(for: "your-event-id")
let slrViewController = StreamLayer.createOverlay(
containerViewController: self,
contentView: containerView,
delegate: self
)Signal the ad break
Paused ads are triggered by an ad break. Call StreamLayer.startADBreak() when the viewer pauses and StreamLayer.stopADBreak() when playback resumes. Add a delay so brief or accidental pauses don't start a break — a 3–5 second delay is common. Use a DispatchWorkItem you can cancel if playback resumes first.
private var adBreakWorkItem: DispatchWorkItem?
func handlePlayingState() {
StreamLayer.stopADBreak()
adBreakWorkItem?.cancel()
adBreakWorkItem = nil
}
func handlePausedState() {
let workItem = DispatchWorkItem {
StreamLayer.startADBreak()
}
DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: workItem)
adBreakWorkItem = workItem
}When the ad break starts, StreamLayer presents a paused ad from the active delivery source (below) automatically — no additional call or delegate is required. See Pause Advertising for more on wiring startADBreak / stopADBreak to your player.
Choose the delivery source
setPausedAdDelivery(_:) selects where paused ads come from. The SDK owns the delivery lifecycle (auction, caching, expiry, presentation); you only choose the source.
public static func setPausedAdDelivery(_ delivery: SLRPausedAdDelivery)public enum SLRPausedAdDelivery {
case standard
case prebid(SLRPrebidPlacement)
}Studio (standard) — the default
.standard delivers server-driven paused ads configured in StreamLayer Studio, with inventory and rotation managed for you. It is the default — paused ads work on ad breaks without calling setPausedAdDelivery at all. Call it explicitly to switch back from Prebid:
StreamLayer.setPausedAdDelivery(.standard)Prebid
.prebid(_:) enables an SDK-managed Prebid programmatic source. StreamLayer preloads a winning bid so an ad is ready the instant an ad break starts, then presents it through the VAST sidebar / L-bar pipeline. Opt in once with an SLRPrebidPlacement:
func enablePrebidPausedAds() {
ATTrackingManager.requestTrackingAuthorization { _ in
DispatchQueue.main.async {
StreamLayer.setPausedAdDelivery(
.prebid(SLRPrebidPlacement(
storedRequestId: "sl-pause-tvos",
demandSignals: {
SLRPrebidDemandSignals(
advertisingIdentifier: ASIdentifierManager.shared().advertisingIdentifier.uuidString,
isLimitAdTrackingEnabled: false
)
}
))
)
}
}
}SLRPrebidPlacement fields:
storedRequestId— the stored request configured for your placement in the auction backend.demandSignals— a closure returning per-request signals (advertising identifier, limit-ad-tracking flag), re-read fresh on every auction. ReturnSLRPrebidDemandSignals.limitedwhen the user has not granted tracking authorization.cannedResponse— an optionalSLRPrebidCannedResponsefor testing against a stored auction response instead of a live auction.
// Testing against a stored auction response
StreamLayer.setPausedAdDelivery(
.prebid(SLRPrebidPlacement(
storedRequestId: "sl-pause-tvos",
cannedResponse: SLRPrebidCannedResponse(
storedAuctionResponseId: "mock_video_1",
bidderPlacements: ["appnexus": 13144370]
)
))
)Request tracking authorization (
ATTrackingManager.requestTrackingAuthorization) before building live demand signals so the advertising identifier is available. The sameSLRPrebidPlacementtype also drives on-demandshowAd(.prebid(_:))in Exposed Ads (tvOS); the difference is thatsetPausedAdDelivery(.prebid)preloads a bid for automatic ad-break delivery, whereasshowAd(.prebid)runs a fresh auction on demand.
Resume playback
When the viewer dismisses the paused ad, the SDK calls streamLayerDelegateAdDidFinish(_:) on your StreamLayerTVOSDelegate, passing an SLRAdInfo. For paused ads info.isPausedAd is true — resume playback and clear the ad break there. The optional streamLayerDelegateAdDidStart(_:) fires when the ad begins presenting.
extension YourViewController: StreamLayerTVOSDelegate {
func streamLayerDelegateAdDidStart(_ info: SLRAdInfo) {}
func streamLayerDelegateAdDidFinish(_ info: SLRAdInfo) {
guard info.isPausedAd else { return }
player.play()
StreamLayer.stopADBreak()
adBreakWorkItem?.cancel()
adBreakWorkItem = nil
}
func streamLayerDelegateUpdateDuckingState(_ enabled: Bool) {
player.volume = enabled ? 0.2 : 1.0
}
}Related
- Pause Advertising — Wiring
startADBreak/stopADBreakto your player - Exposed Ads (tvOS) — Host-driven, on-demand ad units you control
- Pause Ads — overview — Cross-platform overview of the pause ads format
- tvOS Integration Guide — SDK installation and initialization for tvOS
Updated 8 days ago
