The MediaTracker API allows you to track video content events with Permutive. Use this for tracking video playback, engagement, and completion metrics.
Connected TV Integration Video event tracking described below is available once the connected TV integration has been enabled. Please contact your Customer Success Manager (CSM) to enable this feature.
Overview
MediaTracker provides comprehensive video tracking capabilities:
Automatic lifecycle tracking - Tracks Videoview and VideoComplete events
Engagement metrics - Measures time spent watching and completion percentage
Custom events - Track custom video-related events
Rich metadata - Support for extensive video properties
Video Events
MediaTracker automatically tracks two event types:
1. Videoview Event
Tracked when MediaTracker is created:
Event: Videoview
Properties:
- [your custom properties]
- [standard video properties]
2. VideoComplete Event
Tracked when video is stopped:
Event: VideoComplete
Properties:
- engaged_time: Total time spent watching (seconds)
- completion: Percentage of video viewed (0.0 - 1.0)
- [your custom properties]
- [standard video properties]
📘 Note
The completion property requires a known duration to be set. Pass durationMilliseconds when creating the MediaTracker.
Basic Usage
Create a MediaTracker when video playback begins:
import Permutive_iOS
import AVKit
class VideoPlayerViewController : UIViewController {
private var mediaTracker: MediaTrackerProtocol?
private var player: AVPlayer!
override func viewDidLoad () {
super . viewDidLoad ()
mediaTracker = try ? Permutive. shared . createMediaTracker (
durationMilliseconds : 120000 , // 2 minutes
videoProperties : MediaTracker. VideoProperties (
title : "Sample Video" ,
genre : [ "Documentary" ],
contentType : [ "Education" ],
runtime : 120
)
)
}
override func viewWillDisappear ( _ animated : Bool ) {
super . viewWillDisappear (animated)
mediaTracker?. stop () // Important: Always stop to send VideoComplete
}
}
Expected Usage Flow
Create MediaTracker instance with properties
Call play() when playback begins
Track buffering with pause() / play()
Track scrubbing with play(position)
Call stop() when playback completes
⚠️ Single Instance Limitation
Only a single instance of PageTracker or MediaTracker is available at any time. Creating a new tracker automatically stops any existing one.
Playback Control Methods
play()
Call when video starts playing:
try ? mediaTracker?. play ()
play(positionMs)
Call when playback position changes (e.g., seeking):
try ? mediaTracker?. play ( positionMs : 30000 ) // Seek to 30 seconds
pause()
Call when video is paused or buffering:
stop()
Call when video playback completes or user exits:
mediaTracker?. stop () // Sends VideoComplete event
⚠️ Important: Always Call stop()
Failing to call stop() will prevent VideoComplete events from being tracked.
Video Properties
Standard Video Properties
The SDK provides standard video properties for consistent tracking:
MediaTracker. VideoProperties (
title : "Video Title" ,
genre : [ "Action" , "Thriller" ],
contentType : [ "Movie" , "Feature" ],
ageRating : "PG-13" ,
runtime : 7200 , // Runtime in seconds
country : "US" ,
originalLanguage : "en" ,
audioLanguage : "en" ,
areSubtitlesEnabled : true ,
subtitlesLanguage : "en" ,
seasonNumber : 1 ,
episodeNumber : 5 ,
consecutiveEpisodes : 3 ,
iabCategories : [ "IAB1" , "IAB2" ]
)
Property Reference
Property Type Description title String Video title genre [String] Video genres (e.g., “Drama”, “Comedy”) contentType [String] Content types (e.g., “Movie”, “Series”) ageRating String Age rating (e.g., “PG-13”, “TV-MA”) runtime Int Runtime in seconds country String Origin country originalLanguage String Original language code audioLanguage String Audio language code areSubtitlesEnabled Bool Whether subtitles are enabled subtitlesLanguage String Subtitle language code seasonNumber Int Season number (for series) episodeNumber Int Episode number (for series) consecutiveEpisodes Int Consecutive episodes watched iabCategories [String] IAB content taxonomy categories
💡 Best Practice
Track as many properties as possible to enable richer cohort creation and insights.
Page Properties (Optional)
If the video is displayed within a page context (e.g., embedded in article), include page properties:
mediaTracker = try ? Permutive. shared . createMediaTracker (
durationMilliseconds : 120000 ,
videoProperties : MediaTracker. VideoProperties (
title : "Tutorial Video"
),
pageProperties : MediaTracker. PageProperties (
title : "How to Use iOS SDK" ,
url : URL ( string : "https://example.com/tutorial" ),
referrer : URL ( string : "https://example.com/docs" )
)
)
PageProperties Reference
Property Type Description title String Page title url URL Page URL referrer URL Referring page URL
Providing a url in PageProperties enables contextual cohort generation for the video content.
Custom Properties
Add custom properties specific to your event schema:
import Permutive_iOS
mediaTracker = try ? Permutive. shared . createMediaTracker (
durationMilliseconds : 120000 ,
videoProperties : videoProps,
pageProperties : nil ,
customEventProperties : try EventProperties ([
"video_id" : "vid_12345" ,
"playlist_id" : "playlist_abc" ,
"is_premium_content" : true ,
"content_partner" : "PartnerXYZ"
])
)
Complete Example
import Permutive_iOS
import AVKit
class VideoPlayerViewController : UIViewController {
private var mediaTracker: MediaTrackerProtocol?
private var player: AVPlayer!
private var playerObserver: Any ?
override func viewDidLoad () {
super . viewDidLoad ()
// Set up AVPlayer
setupPlayer ()
}
private func setupPlayer () {
let url = URL ( string : "https://example.com/video.mp4" )!
player = AVPlayer ( url : url)
let playerViewController = AVPlayerViewController ()
playerViewController. player = player
addChild (playerViewController)
view. addSubview (playerViewController. view )
playerViewController. view . frame = view. bounds
// Get duration once available, then set up tracker
player. currentItem ?. asset . loadValuesAsynchronously ( forKeys : [ "duration" ]) { [ weak self ] in
guard let self = self ,
let duration = self .player.currentItem?.duration,
!duration.isIndefinite else { return }
let durationMs = Int64 ( CMTimeGetSeconds (duration) * 1000 )
DispatchQueue. main . async {
self . mediaTracker = try ? Permutive. shared . createMediaTracker (
durationMilliseconds : durationMs,
videoProperties : MediaTracker. VideoProperties (
title : "Introduction to iOS" ,
genre : [ "Educational" , "Technology" ],
contentType : [ "Tutorial" ],
ageRating : "G" ,
runtime : 180 ,
country : "US" ,
originalLanguage : "en" ,
audioLanguage : "en" ,
areSubtitlesEnabled : false ,
iabCategories : [ "IAB19" ]
),
pageProperties : MediaTracker. PageProperties (
title : "iOS Tutorial Page" ,
url : URL ( string : "https://example.com/tutorials/ios" ),
referrer : URL ( string : "https://example.com/home" )
),
customEventProperties : try ? EventProperties ([
"video_id" : "vid_tutorial_001" ,
"is_premium" : false
])
)
self . observePlayer ()
}
}
}
private func observePlayer () {
playerObserver = player. observe (\. timeControlStatus ) { [ weak self ] player, _ in
switch player.timeControlStatus {
case . playing :
try ? self ?. mediaTracker ?. play ()
case . paused :
self ?. mediaTracker ?. pause ()
case . waitingToPlayAtSpecifiedRate :
break // Buffering
@unknown default :
break
}
}
}
override func viewWillDisappear ( _ animated : Bool ) {
super . viewWillDisappear (animated)
mediaTracker?. stop () // Send VideoComplete event
}
deinit {
if let observer = playerObserver {
player. removeTimeObserver (observer)
}
}
}
Tracking Custom Events
Track custom video-related events using the track() method:
// User shares video
try ? mediaTracker?. track (
"VideoShared" ,
properties : EventProperties ([
"share_method" : "twitter" ,
"video_position_seconds" : 45
])
)
// User adds to favorites
try ? mediaTracker?. track ( "VideoAddedToFavorites" )
// Quality changed
try ? mediaTracker?. track (
"VideoQualityChanged" ,
properties : EventProperties ([
"from_quality" : "720p" ,
"to_quality" : "1080p"
])
)
Video Ad Tracking
If you need to track video ads within video content, use the AdTracker API:
// Create ad tracker from video tracker
let adTracker = try ? mediaTracker?. trackAdView (
durationMs : 15000 , // 15 second ad
adProperties : AdTracker. AdProperties (
title : "Product Ad" ,
durationInSeconds : 15 ,
isMuted : false ,
campaignId : "campaign_123" ,
creativeId : "creative_456"
)
)
// Track ad playback
try ? adTracker?. play ()
// ... ad plays ...
adTracker?. completion ()
See Video Ad Tracking for complete documentation.
Use Cases
mediaTracker = try ? Permutive. shared . createMediaTracker (
durationMilliseconds : movieDurationMs,
videoProperties : MediaTracker. VideoProperties (
title : movie. title ,
genre : movie. genres ,
contentType : [ "Movie" , "Feature" ],
ageRating : movie. rating ,
runtime : movie. runtimeSeconds ,
country : movie. country ,
originalLanguage : movie. language ,
iabCategories : movie. iabCategories
)
)
mediaTracker = try ? Permutive. shared . createMediaTracker (
durationMilliseconds : tutorialDurationMs,
videoProperties : MediaTracker. VideoProperties (
title : tutorial. title ,
genre : [ "Educational" ],
contentType : [ "Tutorial" ],
runtime : tutorial. runtimeSeconds
),
customEventProperties : try ? EventProperties ([
"course_id" : tutorial. courseId ,
"module_id" : tutorial. moduleId ,
"difficulty_level" : tutorial. difficulty
])
)
mediaTracker = try ? Permutive. shared . createMediaTracker (
durationMilliseconds : episodeDurationMs,
videoProperties : MediaTracker. VideoProperties (
title : episode. title ,
genre : series. genres ,
contentType : [ "Series" , "Episode" ],
seasonNumber : episode. season ,
episodeNumber : episode. number ,
consecutiveEpisodes : userWatchData. consecutiveCount ,
runtime : episode. runtimeSeconds
)
)
tvOS Considerations
tvOS Note: MediaTracker works identically on tvOS. Use the same APIs with your TVUIKit video player or AVPlayerViewController.
// tvOS with AVPlayerViewController
class TVVideoPlayerViewController : UIViewController {
private var mediaTracker: MediaTrackerProtocol?
override func viewDidLoad () {
super . viewDidLoad ()
let playerVC = AVPlayerViewController ()
playerVC. player = AVPlayer ( url : videoURL)
present (playerVC, animated : true ) {
self . setupMediaTracker ()
playerVC. player ?. play ()
}
}
}
Troubleshooting
VideoComplete Not Tracked
Problem: VideoComplete events not appearing.Cause: stop() not called.Solution: Always call stop() in viewWillDisappear:override func viewWillDisappear ( _ animated : Bool ) {
super . viewWillDisappear (animated)
mediaTracker?. stop () // REQUIRED
}
Engagement Time Incorrect
Problem: Engaged time is inaccurate.Causes:
Not calling play() when video plays
Not calling pause() when video pauses/buffers
Solution: Track all play/pause events:playerObserver = player. observe (\. timeControlStatus ) { [ weak self ] player, _ in
switch player.timeControlStatus {
case . playing :
try ? self ?. mediaTracker ?. play ()
case . paused :
self ?. mediaTracker ?. pause ()
default :
break
}
}
Completion Percentage Missing
Problem: completion property is missing or nil.Cause: Duration not provided when creating MediaTracker.Solution: Always provide durationMilliseconds:mediaTracker = try ? Permutive. shared . createMediaTracker (
durationMilliseconds : videoDurationMs // REQUIRED for completion
)
Problem: Error about multiple trackers.Cause: Creating new PageTracker or MediaTracker without closing existing one.Solution: Close existing tracker first:// Close old tracker
mediaTracker?. stop ()
// Now create new tracker
mediaTracker = try ? Permutive. shared . createMediaTracker (...)
See Common Errors for more troubleshooting.
Best Practices
Always call stop() when video finishes
Provide duration for completion tracking
Track play/pause events accurately
Include as many video properties as possible
Close tracker before creating a new one
Track buffering with pause/play
Forget to call stop()
Create multiple MediaTrackers simultaneously
Skip play/pause lifecycle events
Omit video duration
Video Ad Tracking Track video advertisements
Page Tracking Track non-video content
Event Properties Custom properties
Issues Solve common issues
API Reference
play() - Start/resume video playback
play(positionMs: Int64) - Start at specific position
pause() - Pause video playback
stop() - Complete video tracking (sends VideoComplete)
setDuration(durationMs: Int64) - Update video duration
track(_ eventName: String) - Track custom event
track(_ eventName: String, properties: EventProperties) - Track custom event with properties
trackAdView(...) - Create AdTracker for video ad
Permutive.shared.createMediaTracker(durationMilliseconds:videoProperties:pageProperties:customEventProperties:) - Create MediaTracker