Skip to content

Google Cast for Video Workouts beta

This guide explains how to integrate Google Cast in an iOS GoVideoController flow, following the same architecture used in the SampleApp.

Official prerequisites

Before wiring WorkoutKit callbacks, align with Google Cast iOS sender requirements:

  • Use iOS 15+ target baseline.
  • Avoid -Ofast optimization for Cast sender builds; use -Os or standard release optimizations.
  • Call Cast SDK APIs on the main thread.
  • Include Local Network and Bonjour declarations in Info.plist to enable discovery on iOS 14+.

Official references:

1. Add Google Cast SDK dependency

The sample app uses a local Swift Package workaround (GoogleCastSPM) that wraps Google Cast as a binary target.

In your own app, use either this workaround approach or your preferred official Google Cast SDK integration method.

2. Configure app discovery permissions

Google Cast discovery requires local network and Bonjour declarations in Info.plist.

xml
<key>NSLocalNetworkUsageDescription</key>
<string>Local network - usage description</string>

<key>NSBonjourServices</key>
<array>
  <string>_googlecast._tcp</string>
  <string>_ABCDEF123._googlecast._tcp</string>
</array>

Replace ABCDEF123 with your receiver app ID.

3. Initialize the Cast context at app launch

Initialize GCKCastContext in AppDelegate (or app startup entry point):

swift
import GoogleCast

let options = GCKCastOptions(discoveryCriteria: GCKDiscoveryCriteria(applicationID: "ABCDEF123"))
options.physicalVolumeButtonsWillControlDeviceVolume = true
options.disableDiscoveryAutostart = false

let launchOptions = GCKLaunchOptions()
launchOptions.androidReceiverCompatible = true
options.launchOptions = launchOptions

GCKCastContext.setSharedInstanceWith(options)
GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = false

If your app needs persistent background casting behavior, also review GCKCastOptions.suspendSessionsWhenBackgrounded in Google Cast official docs.

4. Attach Cast UI and remote manager in GoVideoController

In your GoVideoController subclass, add a cast button and assign a RemoteManager implementation:

swift
import GoogleCast

final class MonVideoGoController: GoVideoController {
    private let castButton = GCKUICastButton()

    override func viewDidLoad() {
        super.viewDidLoad()

        castButton.tintColor = .white
        topLeftStack.addArrangedSubview(castButton)

        let manager = GoogleCastManager(video: video, metadata: metadata)
        remoteManager = manager
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        if let manager = remoteManager as? GCKSessionManagerListener {
            GCKCastContext.sharedInstance().sessionManager.add(manager)
        }
    }
}

5. Implement GoogleCastManager as a RemoteManager

The sample implementation subclasses WorkoutKit RemoteManager and maps WorkoutKit playback calls to Google Cast commands.

Key responsibilities:

  • Expose currentTime via remoteMediaClient.approximateStreamPosition()
  • map Google Cast player state to RemoteMediaPlayerState
  • implement startMedia(at:with:), playMedia(), pauseMedia(promptTitle:promptCaption:), seekMedia(to:), and disconnect()
  • build GCKMediaInformation from WorkoutKit Video + VideoMetadata
  • pass WorkoutKit UI state through Cast custom payload data
swift
final class GoogleCastManager: RemoteManager {
    override func startMedia(at time: CMTime, with mode: WorkoutStateMode) {
        // Build GCKMediaInformation and call client.loadMedia(...)
    }

    override func playMedia() { /* client.play(...) */ }
    override func pauseMedia(promptTitle: String? = nil, promptCaption: String? = nil) { /* client.pause(...) */ }
    override func seekMedia(to time: CMTime) { /* client.seek(...) */ }
    override func disconnect() { /* sessionManager.endSession() */ }
}

6. Bridge Cast callbacks to WorkoutKit remote delegate

The sample manager implements:

  • GCKSessionManagerListener to detect session start/resume/end and attach channel/listeners
  • GCKRemoteMediaClientListener to push media status updates back into WorkoutKit

Important callback forwarding calls include:

  • delegate?.remoteSessionDidStart()
  • delegate?.remoteSessionDidResume()
  • delegate?.remoteSessionDidEnd(at:)
  • delegate?.remoteSessionCanStartMedia()
  • delegate?.remoteSessionDidUpdateMediaStatus(time:hasMedia:state:)

This is what keeps GoVideoController and the cast receiver synchronized.

7. Production hardening checklist

  • Replace placeholder receiver ID (ABCDEF123) and namespace (urn:x-cast:com.yourapp.cast).
  • Ensure receiver app understands the custom payload fields sent by your manager.
  • Remove listeners appropriately if you add symmetrical cleanup hooks (e.g. in viewWillDisappear).
  • Keep all token/API concerns in host app code; the Cast manager should focus on media transport and state synchronization.

Source references