Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.permutive.com/llms.txt

Use this file to discover all available pages before exploring further.

Track video ad events to measure ad engagement and enable ad-based cohort qualification.

Tracking Ad Events

IMA Integration

Overview

iOS / tvOS does not have a native ad tracker. Unlike Android’s VideoAdTracker, iOS does not provide a typed API for ad tracking. Emit the standard VideoAdView, VideoAdCompletion, and VideoAdClicked events using Permutive.shared.track(event:properties:) with properties nested under an ad parent per the canonical schema.
The three schema events for ad tracking are:
EventWhen to emit
VideoAdViewAd playback begins
VideoAdCompletionAd finishes playing
VideoAdClickedUser clicks the ad
All three emit ad.* properties (and optionally a video.* parent for the content the ad ran against). See the schema reference below.

Tracking Ad Events

VideoAdView

Emit when the ad begins playback:
import Permutive_iOS

func trackAdView(ad: Ad) {
    try? Permutive.shared.track(
        event: "VideoAdView",
        properties: try? EventProperties([
            "ad": [
                "title": ad.title,
                "duration": ad.durationInSeconds,
                "muted": ad.isMuted,
                "campaign_id": ad.campaignId,
                "creative_id": ad.creativeId
            ]
        ])
    )
}

VideoAdCompletion

Emit when the ad finishes:
func trackAdCompletion(ad: Ad) {
    try? Permutive.shared.track(
        event: "VideoAdCompletion",
        properties: try? EventProperties([
            "ad": [
                "title": ad.title,
                "duration": ad.durationInSeconds,
                "campaign_id": ad.campaignId,
                "creative_id": ad.creativeId
            ]
        ])
    )
}

VideoAdClicked

Emit when the user clicks through:
func trackAdClicked(ad: Ad) {
    try? Permutive.shared.track(
        event: "VideoAdClicked",
        properties: try? EventProperties([
            "ad": [
                "title": ad.title,
                "campaign_id": ad.campaignId,
                "creative_id": ad.creativeId
            ]
        ])
    )
}
Skipped or errored ads: the schema defines only VideoAdView, VideoAdCompletion, and VideoAdClicked. Treat a skip as no completion event (no VideoAdCompletion). If you need to capture skip/error signals for your own analytics, use a custom event name and document it in your workspace schema.

Combining with Content Tracking

Pause the content video tracker during ad breaks so engagement time isn’t counted twice:
class VideoPlayerController {
    private var mediaTracker: (NSObject & MediaTrackerProtocol)?
    private var isPlayingAd = false

    // Content tracking
    func onContentPlay() {
        guard !isPlayingAd else { return }
        mediaTracker?.play()
    }

    func onContentPause() {
        guard !isPlayingAd else { return }
        mediaTracker?.pause()
    }

    // Ad tracking
    func onAdBreakStarted() {
        isPlayingAd = true
        mediaTracker?.pause()  // Pause content engagement during ads
    }

    func onAdBreakEnded() {
        isPlayingAd = false
        // Content resumes via onContentPlay()
    }

    func onAdStarted(_ ad: Ad) {
        try? Permutive.shared.track(
            event: "VideoAdView",
            properties: try? EventProperties([
                "ad": [
                    "title": ad.title,
                    "duration": ad.durationInSeconds,
                    "campaign_id": ad.campaignId,
                    "creative_id": ad.creativeId
                ]
            ])
        )
    }

    func onAdCompleted(_ ad: Ad) {
        try? Permutive.shared.track(
            event: "VideoAdCompletion",
            properties: try? EventProperties([
                "ad": [
                    "title": ad.title,
                    "campaign_id": ad.campaignId,
                    "creative_id": ad.creativeId
                ]
            ])
        )
    }
}

Google IMA Integration

Hook IMAAdsManagerDelegate events to emit Permutive ad events:
import GoogleInteractiveMediaAds
import Permutive_iOS

class VideoPlayerViewController: UIViewController, IMAAdsManagerDelegate {

    func adsManager(_ adsManager: IMAAdsManager, didReceive event: IMAAdEvent) {
        guard let imaAd = event.ad else { return }

        switch event.type {
        case .STARTED:
            emitAdView(imaAd)
        case .COMPLETE:
            emitAdCompletion(imaAd)
        case .CLICKED:
            emitAdClicked(imaAd)
        default:
            break
        }
    }

    private func emitAdView(_ ad: IMAAd) {
        try? Permutive.shared.track(
            event: "VideoAdView",
            properties: try? EventProperties([
                "ad": [
                    "title": ad.adTitle,
                    "duration": Int(ad.duration),
                    "campaign_id": ad.adId,
                    "creative_id": ad.creativeID ?? ""
                ]
            ])
        )
    }

    private func emitAdCompletion(_ ad: IMAAd) {
        try? Permutive.shared.track(
            event: "VideoAdCompletion",
            properties: try? EventProperties([
                "ad": [
                    "title": ad.adTitle,
                    "campaign_id": ad.adId,
                    "creative_id": ad.creativeID ?? ""
                ]
            ])
        )
    }

    private func emitAdClicked(_ ad: IMAAd) {
        try? Permutive.shared.track(
            event: "VideoAdClicked",
            properties: try? EventProperties([
                "ad": [
                    "title": ad.adTitle,
                    "campaign_id": ad.adId,
                    "creative_id": ad.creativeID ?? ""
                ]
            ])
        )
    }
}
IMA’s adId is the line-item / ad reference. Map it to whichever Permutive schema field reflects your taxonomy (campaign_id shown here, but use creative_id if that’s a closer match).

Schema Reference

The canonical schema for ad events is defined in CTV Video Tracking. Summary:
PropertyTypeDescription
ad.titleStringAd title
ad.durationIntAd duration in seconds
ad.mutedBoolWhether the ad was muted
ad.campaign_idStringCampaign identifier
ad.creative_idStringCreative identifier
For VideoAdCompletion, the SDK does not auto-aggregate engagement on iOS (no native ad tracker means no aggregations.VideoAdEngagement.* enrichment) — emit only the ad.* properties.
Ensure property names match your Permutive workspace schema. Mismatched properties are rejected.

tvOS Considerations

tvOS Note: Ad tracking on tvOS uses the same Permutive.shared.track(event:properties:) API. Use it from your TVMLKit or UIKit video player.

Best Practices

  • Emit VideoAdView only when the ad is actually visible / playing
  • Nest all ad properties under an ad parent (e.g., ad.title, not flat ad_title)
  • Use the canonical event names — VideoAdView, VideoAdCompletion, VideoAdClicked
  • Pause the content MediaTracker during ad breaks so content engagement isn’t inflated
  • Pass duration in seconds (Int), not milliseconds

Troubleshooting

Problem: Ad events do not show in the Permutive dashboard.Solutions:
  • Confirm Permutive.configure(...) ran before track(...) is called
  • Verify event names match: VideoAdView, VideoAdCompletion, VideoAdClicked
  • Check that ad.* property nesting matches your workspace schema
  • Enable debug logging and watch for schema validation errors
Problem: Logs show events being rejected.Solutions:
  • Ensure all properties are nested under ad (not at the top level)
  • Check property types — duration must be Int, muted must be Bool
  • Verify the event name spelling exactly matches the schema
Problem: Permutive targeting does not appear on GAM ad requests.Solutions:
  • Call googleCustomTargeting(adTargetable:) before the ad request
  • Pass an active MediaTracker or PageTracker as adTargetable so targeting reflects the current session
  • Confirm the targeting dictionary is applied to the GAMRequest

Video Tracking

Track video content

CTV Video Schema

Canonical event and property reference

Google Ad Manager

GAM integration

Event Tracking

Custom event tracking