Track video advertisement viewing to understand ad engagement and enable ad-based segmentation.
Overview
Video ad tracking enables:
- Ad impression tracking when ads start
- Ad completion tracking when ads finish
- Ad engagement metrics for targeting
- Ad-based cohort qualification
Tracking Video Ads
Track video ad events using the standard event tracking API:
import Permutive_iOS
class VideoAdTracker {
func trackAdImpression(ad: Ad, position: AdPosition) {
try? Permutive.shared.track(
event: "VideoAdImpression",
properties: try? EventProperties([
"ad_id": ad.id,
"ad_title": ad.title,
"ad_duration": ad.duration,
"ad_position": position.rawValue, // "pre-roll", "mid-roll", "post-roll"
"advertiser": ad.advertiser,
"campaign_id": ad.campaignId
])
)
}
func trackAdComplete(ad: Ad, watchedPercentage: Float) {
try? Permutive.shared.track(
event: "VideoAdComplete",
properties: try? EventProperties([
"ad_id": ad.id,
"ad_title": ad.title,
"watched_percentage": watchedPercentage,
"completed": watchedPercentage >= 0.95
])
)
}
func trackAdSkipped(ad: Ad, skippedAtSeconds: Double) {
try? Permutive.shared.track(
event: "VideoAdSkipped",
properties: try? EventProperties([
"ad_id": ad.id,
"skipped_at_seconds": skippedAtSeconds,
"total_duration": ad.duration
])
)
}
func trackAdClicked(ad: Ad) {
try? Permutive.shared.track(
event: "VideoAdClicked",
properties: try? EventProperties([
"ad_id": ad.id,
"click_url": ad.clickThroughUrl
])
)
}
}
enum AdPosition: String {
case preRoll = "pre-roll"
case midRoll = "mid-roll"
case postRoll = "post-roll"
}
Google IMA Integration
Integrate with Google Interactive Media Ads (IMA) SDK:
import GoogleInteractiveMediaAds
import Permutive_iOS
class VideoPlayerViewController: UIViewController, IMAAdsLoaderDelegate, IMAAdsManagerDelegate {
private var adsLoader: IMAAdsLoader!
private var adsManager: IMAAdsManager?
override func viewDidLoad() {
super.viewDidLoad()
setupAdsLoader()
}
private func setupAdsLoader() {
adsLoader = IMAAdsLoader(settings: nil)
adsLoader.delegate = self
}
func requestAds() {
// Include Permutive targeting in ad request
let adRequest = GAMRequest()
adRequest.customTargeting = Permutive.shared.googleCustomTargeting(
adTargetable: pageTracker // Include if using PageTracker
)
let adDisplayContainer = IMAAdDisplayContainer(
adContainer: adContainerView,
viewController: self
)
let request = IMAAdsRequest(
adTagUrl: adTagUrl,
adDisplayContainer: adDisplayContainer,
contentPlayhead: contentPlayhead,
userContext: nil
)
adsLoader.requestAds(with: request)
}
// MARK: - IMAAdsManagerDelegate
func adsManager(_ adsManager: IMAAdsManager, didReceive event: IMAAdEvent) {
switch event.type {
case .STARTED:
trackAdStarted(event.ad)
case .COMPLETE:
trackAdCompleted(event.ad)
case .SKIPPED:
trackAdSkipped(event.ad)
case .CLICKED:
trackAdClicked(event.ad)
case .FIRST_QUARTILE:
trackAdProgress(event.ad, quartile: 1)
case .MIDPOINT:
trackAdProgress(event.ad, quartile: 2)
case .THIRD_QUARTILE:
trackAdProgress(event.ad, quartile: 3)
default:
break
}
}
private func trackAdStarted(_ ad: IMAAd?) {
guard let ad = ad else { return }
try? Permutive.shared.track(
event: "VideoAdStarted",
properties: try? EventProperties([
"ad_id": ad.adId,
"ad_title": ad.adTitle,
"duration": ad.duration,
"is_skippable": ad.isSkippable,
"ad_system": ad.adSystem,
"advertiser": ad.advertiserName
])
)
}
private func trackAdCompleted(_ ad: IMAAd?) {
guard let ad = ad else { return }
try? Permutive.shared.track(
event: "VideoAdComplete",
properties: try? EventProperties([
"ad_id": ad.adId,
"ad_title": ad.adTitle,
"duration": ad.duration
])
)
}
private func trackAdSkipped(_ ad: IMAAd?) {
guard let ad = ad else { return }
try? Permutive.shared.track(
event: "VideoAdSkipped",
properties: try? EventProperties([
"ad_id": ad.adId,
"ad_title": ad.adTitle
])
)
}
private func trackAdClicked(_ ad: IMAAd?) {
guard let ad = ad else { return }
try? Permutive.shared.track(
event: "VideoAdClicked",
properties: try? EventProperties([
"ad_id": ad.adId
])
)
}
private func trackAdProgress(_ ad: IMAAd?, quartile: Int) {
guard let ad = ad else { return }
try? Permutive.shared.track(
event: "VideoAdProgress",
properties: try? EventProperties([
"ad_id": ad.adId,
"quartile": quartile,
"percentage": quartile * 25
])
)
}
}
Ad Pod Tracking
Track ad pods (multiple ads in sequence):
class AdPodTracker {
func trackAdPodStarted(podIndex: Int, adCount: Int) {
try? Permutive.shared.track(
event: "AdPodStarted",
properties: try? EventProperties([
"pod_index": podIndex,
"ad_count": adCount,
"position": podIndex == 0 ? "pre-roll" : "mid-roll"
])
)
}
func trackAdPodComplete(podIndex: Int, adsWatched: Int, totalAds: Int) {
try? Permutive.shared.track(
event: "AdPodComplete",
properties: try? EventProperties([
"pod_index": podIndex,
"ads_watched": adsWatched,
"total_ads": totalAds,
"completion_rate": Float(adsWatched) / Float(totalAds)
])
)
}
}
Combining with Content Tracking
Track both content and ads in a video player:
class VideoPlayerController {
private var mediaTracker: MediaTrackerProtocol?
private var isPlayingAd = false
// Content tracking
func onContentPlay() {
guard !isPlayingAd else { return }
try? mediaTracker?.play()
}
func onContentPause() {
guard !isPlayingAd else { return }
mediaTracker?.pause()
}
// Ad tracking
func onAdBreakStarted() {
isPlayingAd = true
mediaTracker?.pause() // Pause content tracking during ads
}
func onAdBreakEnded() {
isPlayingAd = false
// Content will resume with onContentPlay()
}
func onAdStarted(_ ad: Ad) {
trackAdImpression(ad: ad)
}
func onAdCompleted(_ ad: Ad) {
trackAdComplete(ad: ad)
}
private func trackAdImpression(ad: Ad) {
try? Permutive.shared.track(
event: "VideoAdImpression",
properties: try? EventProperties([
"ad_id": ad.id,
"ad_title": ad.title
])
)
}
private func trackAdComplete(ad: Ad) {
try? Permutive.shared.track(
event: "VideoAdComplete",
properties: try? EventProperties([
"ad_id": ad.id
])
)
}
}
Event Schema
Common video ad event properties:
| Property | Type | Description |
|---|
ad_id | String | Unique ad identifier |
ad_title | String | Ad title or description |
duration | Number | Ad duration in seconds |
ad_position | String | ”pre-roll”, “mid-roll”, “post-roll” |
advertiser | String | Advertiser name |
campaign_id | String | Campaign identifier |
is_skippable | Boolean | Whether ad can be skipped |
watched_percentage | Number | Percentage of ad watched (0-1) |
Ensure your event properties match your Permutive dashboard schema. Mismatched properties result in event rejection.
tvOS Considerations
tvOS Note: Video ad tracking works the same on tvOS. Use the same event tracking APIs with your TVML or UIKit video player.
Best Practices
- Track ad impressions when ads actually start playing
- Include ad position (pre-roll, mid-roll, post-roll)
- Track quartile progress for engagement analysis
- Pause content tracking during ad breaks
- Include meaningful ad metadata
- Don’t track impression before ad is visible
- Don’t include PII in ad properties
- Don’t track duplicate events
- Don’t mix content and ad events incorrectly
Troubleshooting
Problem: Video ad events not in dashboard.Solutions:
- Enable debug logging
- Check event names match schema
- Verify SDK is initialized
- Look for schema validation errors
Targeting not working for ads
Problem: Permutive targeting not applied to ad requests.Solutions:
- Ensure
googleCustomTargeting is called before ad request
- Verify PageTracker is active when requesting ads
- Check targeting dictionary is applied to GAMRequest