Preroll Ads
Preroll ads are video ads that play before your content in a video player container. The SDK renders and auto-plays the ad inside a UIView you provide, using Google IMA — there is no separate "show" call. Users can skip the ad when the VAST creative allows it.
When the ad finishes, is skipped, or fails, you start your own content playback — typically from the delegate callbacks (onAdCompleted / onAdSkipped / onAdFailed).
Each ad format uses a Zone ID to identify the ad placement. Zone IDs are configured in the Empower dashboard.
Note: The preroll API is Swift-only — loading a preroll from Objective-C is not supported. The delegate is optional; use it only if you need to react to ad states for analytics, UI, or to trigger your content player.
Container Setup
Add a UIView to your layout as the video ad container:
- Storyboard
- Programmatic
Create a UIView in your storyboard and connect it as an IBOutlet:
@IBOutlet weak var playerView: UIView!
let playerView = UIView()
playerView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(playerView)
NSLayoutConstraint.activate([
playerView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
playerView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
playerView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
playerView.heightAnchor.constraint(equalTo: playerView.widthAnchor, multiplier: 9.0/16.0)
])
Loading a Preroll Ad
Call loadPrerollAd(zoneId:playerView:muted:delegate:). The SDK loads the ad and auto-plays it inside playerView as soon as it is ready — you do not call a separate show method. If the SDK is not fully initialized yet, the request is queued and runs automatically once it is ready.
- UIKit
- SwiftUI
import EmpowerMobileAds
class VideoViewController: UIViewController {
@IBOutlet weak var playerView: UIView!
private let zoneId = "YOUR_PREROLL_ZONE_ID"
override func viewDidLoad() {
super.viewDidLoad()
EMAManager.shared.loadPrerollAd(
zoneId: zoneId,
playerView: playerView,
muted: false,
delegate: self
)
}
private func startContentPlayback() {
// Start your own AVPlayer / video content here.
}
deinit {
EMAManager.shared.destroyPreroll(zoneId: zoneId)
}
}
extension VideoViewController: PrerollAdDelegate {
func onAdReady() {} // loaded, about to auto-play
func onAdStarted() {} // playback started
func onAdCompleted() { startContentPlayback() }
func onAdSkipped() { startContentPlayback() }
func onAdFailed(error: String) { startContentPlayback() }
func onAdClicked() {}
}
Wrap a container view controller in UIViewControllerRepresentable:
import SwiftUI
import EmpowerMobileAds
struct VideoPlayerScreen: View {
@State private var adFinished = false
var body: some View {
ZStack {
if adFinished {
ContentVideoPlayer()
} else {
PrerollAdView(zoneId: "YOUR_PREROLL_ZONE_ID") {
adFinished = true
}
}
}
}
}
struct PrerollAdView: UIViewControllerRepresentable {
let zoneId: String
let onFinished: () -> Void
func makeUIViewController(context: Context) -> PrerollAdViewController {
PrerollAdViewController(zoneId: zoneId, onFinished: onFinished)
}
func updateUIViewController(_ uiViewController: PrerollAdViewController, context: Context) {}
}
final class PrerollAdViewController: UIViewController, PrerollAdDelegate {
private let zoneId: String
private let onFinished: () -> Void
init(zoneId: String, onFinished: @escaping () -> Void) {
self.zoneId = zoneId
self.onFinished = onFinished
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
override func viewDidLoad() {
super.viewDidLoad()
EMAManager.shared.loadPrerollAd(zoneId: zoneId, playerView: view, delegate: self)
}
// A single terminal callback drives the transition to content.
func onAdCompleted() { onFinished() }
func onAdSkipped() { onFinished() }
func onAdFailed(error: String) { onFinished() }
func onAdReady() {}
func onAdStarted() {}
func onAdClicked() {}
deinit { EMAManager.shared.destroyPreroll(zoneId: zoneId) }
}
That's it — the SDK loads the ad and auto-plays it. Use the delegate's terminal callbacks (onAdCompleted / onAdSkipped / onAdFailed) to start your content video.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
zoneId | String | Yes | — | The zone ID for the preroll placement |
playerView | UIView | Yes | — | The container view where the ad is rendered and auto-played |
muted | Bool | No | false | Whether the ad should start muted |
delegate | PrerollAdDelegate? | No | nil | Delegate to receive preroll ad events |
Handling Preroll Events (Optional)
Adopt PrerollAdDelegate to react to the ad lifecycle. All methods are called on the main thread.
extension VideoViewController: PrerollAdDelegate {
func onAdReady() {
// Ad loaded and about to auto-play.
}
func onAdStarted() {
// Ad playback started.
}
func onAdCompleted() {
// Ad finished — start your content video.
startContentPlayback()
}
func onAdSkipped() {
// User skipped the ad — start your content video.
startContentPlayback()
}
func onAdFailed(error: String) {
// Ad failed to load or play — start your content video.
print("Preroll failed: \(error)")
startContentPlayback()
}
func onAdClicked() {
// User tapped the ad.
}
}
Lifecycle Management
Preroll zones are managed by EMAManager.shared and keyed by zoneId. Pause, resume, and destroy them through the manager — there is no per-ad handle to hold onto:
// Pause playback (e.g., the view scrolled off-screen or the app backgrounded)
EMAManager.shared.pausePreroll(zoneId: zoneId)
// Resume playback
EMAManager.shared.resumePreroll(zoneId: zoneId)
// Release the zone when the ad is no longer needed (e.g., in deinit)
EMAManager.shared.destroyPreroll(zoneId: zoneId)
| Method | Description |
|---|---|
pausePreroll(zoneId:) | Pauses playback for the given zone |
resumePreroll(zoneId:) | Resumes playback for the given zone |
destroyPreroll(zoneId:) | Releases IMA resources and removes the ad for the given zone |
Loading Overlay
By default the SDK shows a built-in loading overlay (shimmer + progress) over playerView while the ad loads. To manage your own loading UI instead, disable it before loading:
EMAManager.shared.showPrerollLoadingOverlay = false
Best Practices
- Start content in the terminal callbacks — begin your own playback in
onAdCompleted,onAdSkipped, andonAdFailedso the user always reaches the content whether the ad succeeds, is skipped, or fails. - Destroy on teardown — call
destroyPreroll(zoneId:)indeinit(or when leaving the screen) to release IMA resources. - Mute in silent contexts — pass
muted: truewhen the video auto-plays in a feed or other silent context. - Frequency capping — the SDK respects server-configured session frequency caps; a
zoneIdthat is capped will reportonAdFailed/no fill, so always have the content-fallback path wired up.
Ad Event Reference
| Callback | Description |
|---|---|
onAdReady() | Ad is loaded and about to auto-play |
onAdStarted() | Ad playback started |
onAdCompleted() | Ad finished playing — start your content |
onAdSkipped() | User skipped the ad — start your content |
onAdFailed(error:) | Ad failed to load or play — start your content |
onAdClicked() | User clicked the ad |