How to Properly Integrate Google IMA SDK for Pre-roll, Mid-roll, and Post-roll Ads in a tvOS App using TVJS and Swift?

I'm working on a tvOS application that plays video content using TVMLKit/TVJS. I'm trying to integrate Google IMA SDK to show pre-roll, mid-roll, and post-roll ads in my app.

Here’s what I’ve done so far:

  • Video playback is handled through JavaScript in application.js.
  • Ads are managed in Swift using Google IMA SDK.
  • I use the evaluateJavaScript bridge to control video playback from Swift.
  • I pause and resume the TVJS player (Player object) using a function like startPlayingVideo(value) from Swift based on the ad lifecycle.

Current Flow:

  • When the video is about to start, I call loadAds() from JS.
  • This presents a Swift ViewController that handles IMA ad requests.
  • On adsManagerDidRequestContentPause, I pause the video using JS via the bridge.
  • On adsManagerDidRequestContentResume, I resume the video.

The Issue:

  • This setup doesn't behave consistently:
  • Sometimes the ad plays in the background and video started playing as well but can not see the ad.
  • Not able to see the post-roll ads

Relevant Code Snippets:

application.js

function startPlayingVideo(value) {
if (playerReference != undefined) {
if (value == true) {
playerReference.play();
else {
playerReference.pause();
   }
  }
}

function playVideo(videoURL) {
setup playerReference, push mediaItem, etc.
loadAds();
player.present();
}

AppDelegate.swift

let loadAds: @convention(block) () -\> Void = {
DispatchQueue.main.async {
let adManagerVC = ViewController()
AppDelegate.tvController?.navigationController.present(adManagerVC, animated: true)
}
}

let updateVideoPlayTime: @convention(block) (Double) -\> Void = { time in
CustomContentPlayhead.shared.currentTime = TimeInterval(time)
}

ViewController.swift

func adsManagerDidRequestContentPause(\_ adsManager: IMAAdsManager) {
showAdUI()
playerViewController.player?.pause()
}

func adsManagerDidRequestContentResume(\_ adsManager: IMAAdsManager) {
hideAdUI()
// Expecting JS video to resume via bridge
}

And yeah my IMSDK Implementation is working fine if I am using it with swift AVPlayer.

What I Need Help With:

  • Best practice for coordinating video playback between JS (Player) and Swift (IMAAdsManager).
  • How can I sync the playhead reliably between JS and Swift?
  • Is there a better way to pause/resume TVJS player from Swift during ad lifecycle?
  • How to reliably insert mid-roll ads when the playback is primarily controlled in JS?

Any insights, code examples, or recommended architecture improvements would be greatly appreciated!