Cast-enable an iOS app

1. Overview

Google Cast logo

This codelab will teach you how to modify an existing iOS video app to cast content on a Google Cast-enabled device.

What is Google Cast?

Google Cast allows users to cast content from a mobile device to a TV. Users can then use their mobile device as a remote control for media playback on the TV.

The Google Cast SDK lets you extend your app to control Google Cast enabled devices (such as a TV or sound system). The Cast SDK allows you to add the necessary UI components based on the Google Cast Design Checklist .

The Google Cast Design Checklist is provided to make the Cast user experience simple and predictable across all supported platforms.

What are we going to be building?

When you have completed this codelab, you will have an iOS video app that will be able to Cast videos to a Google Cast device.

What you'll learn

  • How to add the Google Cast SDK to a sample video app.
  • How to add the Cast button for selecting a Google Cast device.
  • How to connect to a Cast device and launch a media receiver.
  • How to cast a video.
  • How to add a Cast mini controller to your app.
  • How to add an expanded controller.
  • How to provide an introductory overlay.
  • How to customize Cast widgets.
  • How to integrate Cast Connect

What you'll need

  • The latest Xcode .
  • One mobile device with iOS 9 or later (or the Xcode Simulator).
  • A USB data cable to connect your mobile device to your development computer (if using a device).
  • A Google Cast device such as a Chromecast or Android TV configured with internet access.
  • A TV or monitor with HDMI input.
  • A Chromecast with Google TV is required to test Cast Connect integration but is optional for the rest of the Codelab. If you do not have one, feel free to skip the Add Cast Connect Supportstep, towards the end of this tutorial.

Experience

  • You will need to have previous iOS development knowledge.
  • You will also need previous knowledge of watching TV :)

How will you use this tutorial?

Read it through only Read it and complete the exercises

How would you rate your experience with building iOS apps?

Novice Intermediate Proficient

How would you rate your experience with watching TV?

Novice Intermediate Proficient

2. Get the sample code

You can either download all the sample code to your computer...

and unpack the downloaded zip file.

3. Run the sample app

Apple iOS logo

First, let's see what the completed sample app looks like. The app is a basic video player. The user can select a video from a list and can then play the video locally on the device or Cast it to a Google Cast device.

With the code downloaded, the following instructions describe how to open and run the completed sample app in Xcode:

Frequently asked questions

CocoaPods setup

To setup CocoaPods, go to your console and install using the default Ruby available on macOS:

sudo gem install cocoapods

If you have any issues, refer to the official documentation to download and install the dependency manager.

Project setup

  1. Go to your terminal and navigate to the codelab directory.
  2. Install the dependencies from the Podfile.
cd app-done
pod update
pod install
  1. Open Xcode and select Open another project...
  2. Select the CastVideos-ios.xcworkspace file from thefolder icon app-done directory in the sample code folder.

Run the app

Select the target and simulator, and then run the app:

XCode app simulator toolbar

You should see the video app appear after a few seconds.

Be sure to click ‘Allow' when the notification appears about accepting incoming network connects. The Cast icon will not appear if this option is not accepted.

Confirmation dialog asking for permission to accept inbound network connections

Click the Cast button and select your Google Cast device.

Select a video, click on the play button.

The video will start playing on your Google Cast device.

The expanded controller will be displayed. You can use the play/pause button to control the playback.

Navigate back to the list of videos.

A mini controller is now visible at the bottom of the screen.

Illustration of iPhone running CastVideos app with the mini controller appearing at the bottom

Click on the pause button in the mini controller to pause the video on the receiver. Click on the play button in the mini controller to continue playing the video again.

Click on the Cast button to stop casting to the Google Cast device.

4. Prepare the start project

Illustration of iPhone running CastVideos app

We need to add support for Google Cast to the start app you downloaded. Here are some Google Cast terminology that we will be using in this codelab:

  • a senderapp runs on a mobile device or laptop,
  • a receiverapp runs on the Google Cast device.

Project setup

Now you're ready to build on top of the starter project using Xcode:

  1. Go to your terminal and navigate to the codelab directory.
  2. Install the dependencies from the Podfile.
cd app-start
pod update
pod install
  1. Open Xcode and select Open another project...
  2. Select the CastVideos-ios.xcworkspace file from thefolder icon app-start directory in the sample code folder.

App design

The app fetches a list of videos from a remote web server and provides a list for the user to browse. Users can select a video to see the details or play the video locally on the mobile device.

The app consists of two main view controllers: MediaTableViewController and MediaViewController.

MediaTableViewController

This UITableViewController displays a list of videos from a MediaListModel instance. The list of videos and their associated metadata are hosted on a remote server as a JSON file. MediaListModel fetches this JSON and processes it to build a list of MediaItem objects.

A MediaItem object models a video and its associated metadata, such as its title, description, URL for an image, and URL for the stream.

MediaTableViewController creates a MediaListModel instance and then registers itself as a MediaListModelDelegate to be informed when the media metadata has been downloaded so it can load the table view.

The user is presented with a list of video thumbnails with a short description for each video. When an item is selected, the corresponding MediaItem is passed to the MediaViewController .

MediaViewController

This view controller displays the metadata about a particular video and allows the user to play the video locally on the mobile device.

The view controller hosts a LocalPlayerView , some media controls, and a text area to show the description of the selected video. The player covers the top portion of the screen, leaving room for the detailed description of the video beneath The user can play/pause or seek the local video playback.

Frequently asked questions

5. Adding the Cast button

Illustration of the top third of an iPhone running the CastVideos app, showing the Cast button in the upper right-hand corner

A Cast-enabled application displays the Cast button in each of its view controllers. Clicking on the Cast button displays a list of Cast devices which a user can select. If the user was playing content locally on the sender device, selecting a Cast device starts or resumes playback on that Cast device. At any time during a Cast session, the user can click on the Cast button and stop casting your application to the Cast device. The user must be able to connect to or disconnect from the Cast device while in any screen of your application, as described in the Google Cast Design Checklist .

Configuration

The start project requires the same dependencies and Xcode setup as you did for the completed sample app. Return to that section and follow the same steps to add the GoogleCast.framework to the start app project.

Initialization

The Cast framework has a global singleton object, the GCKCastContext , which coordinates all of the framework's activities. This object must be initialized early in the application's lifecycle, typically in the application(_:didFinishLaunchingWithOptions:) method of the app delegate, so that automatic session resumption on the sender application restart can trigger properly and scanning for devices can start.

A GCKCastOptions object must be supplied when initializing the GCKCastContext . This class contains options that affect the behavior of the framework. The most important of these is the receiver application ID, which is used to filter Cast device discovery results and to launch the receiver application when a Cast session is started.

The application(_:didFinishLaunchingWithOptions:) method is also a good place to set up a logging delegate to receive the logging messages from Cast framework. These can be useful for debugging and troubleshooting.

When you develop your own Cast-enabled app, you have to register as a Cast developer and then obtain an application ID for your app. For this codelab, we will be using a sample app ID.

Add the following code to AppDelegate.swift to initialize GCKCastContext with the application ID from the user defaults, and add a logger for the Google Cast framework:

  import 
  
 GoogleCast 
 @UIApplicationMain 
 class 
  
 AppDelegate 
 : 
 UIResponder 
 , 
 UIApplicationDelegate 
 { 
 fileprivate 
 var 
 enableSDKLogging 
 = 
 true 
 ... 
 func 
 application 
 ( 
 _ 
 : 
 UIApplication 
 , 
 didFinishLaunchingWithOptions 
 _ 
 : 
 [ 
 UIApplication 
 . 
 LaunchOptionsKey 
 : 
 Any 
 ] 
 ? 
 ) 
 - 
> Bool 
 { 
 ... 
 let 
 options 
 = 
 GCKCastOptions 
 ( 
 discoveryCriteria 
 : 
 GCKDiscoveryCriteria 
 ( 
 applicationID 
 : 
 kReceiverAppID 
 )) 
 options 
 . 
 physicalVolumeButtonsWillControlDeviceVolume 
 = 
 true 
 GCKCastContext 
 . 
 setSharedInstanceWith 
 ( 
 options 
 ) 
 window 
 ? 
 . 
 clipsToBounds 
 = 
 true 
 setupCastLogging 
 () 
 ... 
 } 
 ... 
 func 
 setupCastLogging 
 () 
 { 
 let 
 logFilter 
 = 
 GCKLoggerFilter 
 () 
 let 
 classesToLog 
 = 
 [ 
 "GCKDeviceScanner" 
 , 
 "GCKDeviceProvider" 
 , 
 "GCKDiscoveryManager" 
 , 
 "GCKCastChannel" 
 , 
 "GCKMediaControlChannel" 
 , 
 "GCKUICastButton" 
 , 
 "GCKUIMediaController" 
 , 
 "NSMutableDictionary" 
 ] 
 logFilter 
 . 
 setLoggingLevel 
 ( 
 . 
 verbose 
 , 
 forClasses 
 : 
 classesToLog 
 ) 
 GCKLogger 
 . 
 sharedInstance 
 () 
 . 
 filter 
 = 
 logFilter 
 GCKLogger 
 . 
 sharedInstance 
 () 
 . 
 delegate 
 = 
 self 
 } 
 } 
 ... 
 // 
 MARK 
 : 
 - 
 GCKLoggerDelegate 
 extension 
 AppDelegate 
 : 
 GCKLoggerDelegate 
 { 
 func 
 logMessage 
 ( 
 _ 
 message 
 : 
 String 
 , 
 at 
 _ 
 : 
 GCKLoggerLevel 
 , 
 fromFunction 
 function 
 : 
 String 
 , 
 location 
 : 
 String 
 ) 
 { 
 if 
 enableSDKLogging 
 { 
 // 
 Send 
 SDK 
 's log messages directly to the console. 
 print 
 ( 
 "\(location): \(function) - \(message)" 
 ) 
 } 
 } 
 } 
 

Cast button

Now that the GCKCastContext is initialized, we need to add the Cast button to allow the user to select a Cast device. The Cast SDK provides a Cast button component called GCKUICastButton as a UIButton subclass. It can be added to the application's title bar by wrapping it in a UIBarButtonItem . We need to add the Cast button to both the MediaTableViewController and the MediaViewController .

Add the following code to MediaTableViewController.swift and MediaViewController.swift :

  import 
  
 GoogleCast 
 @objc 
 ( 
 MediaTableViewController 
 ) 
 class 
  
 MediaTableViewController 
 : 
 UITableViewController 
 , 
 GCKSessionManagerListener 
 , 
 MediaListModelDelegate 
 , 
 GCKRequestDelegate 
 { 
 private 
 var 
 castButton 
 : 
 GCKUICastButton 
 ! 
 ... 
 override 
 func 
 viewDidLoad 
 () 
 { 
 print 
 ( 
 "MediaTableViewController - viewDidLoad" 
 ) 
 super 
 . 
 viewDidLoad 
 () 
 ... 
 castButton 
 = 
 GCKUICastButton 
 ( 
 frame 
 : 
 CGRect 
 ( 
 x 
 : 
 CGFloat 
 ( 
 0 
 ), 
 y 
 : 
 CGFloat 
 ( 
 0 
 ), 
 width 
 : 
 CGFloat 
 ( 
 24 
 ), 
 height 
 : 
 CGFloat 
 ( 
 24 
 ))) 
 // 
 Overwrite 
 the 
 UIAppearance 
 theme 
 in 
 the 
 AppDelegate 
 . 
 castButton 
 . 
 tintColor 
 = 
 UIColor 
 . 
 white 
 navigationItem 
 . 
 rightBarButtonItem 
 = 
 UIBarButtonItem 
 ( 
 customView 
 : 
 castButton 
 ) 
 ... 
 } 
 ... 
 } 
 

Next, add the following code to your MediaViewController.swift :

  import 
  
 GoogleCast 
 @objc 
 ( 
 MediaViewController 
 ) 
 class 
  
 MediaViewController 
 : 
 UIViewController 
 , 
 GCKSessionManagerListener 
 , 
 GCKRemoteMediaClientListener 
 , 
 LocalPlayerViewDelegate 
 , 
 GCKRequestDelegate 
 { 
 private 
 var 
 castButton 
 : 
 GCKUICastButton 
 ! 
 ... 
 override 
 func 
 viewDidLoad 
 () 
 { 
 super 
 . 
 viewDidLoad 
 () 
 print 
 ( 
 "in MediaViewController viewDidLoad" 
 ) 
 ... 
 castButton 
 = 
 GCKUICastButton 
 ( 
 frame 
 : 
 CGRect 
 ( 
 x 
 : 
 CGFloat 
 ( 
 0 
 ), 
 y 
 : 
 CGFloat 
 ( 
 0 
 ), 
 width 
 : 
 CGFloat 
 ( 
 24 
 ), 
 height 
 : 
 CGFloat 
 ( 
 24 
 ))) 
 // 
 Overwrite 
 the 
 UIAppearance 
 theme 
 in 
 the 
 AppDelegate 
 . 
 castButton 
 . 
 tintColor 
 = 
 UIColor 
 . 
 white 
 navigationItem 
 . 
 rightBarButtonItem 
 = 
 UIBarButtonItem 
 ( 
 customView 
 : 
 castButton 
 ) 
 ... 
 } 
 ... 
 } 
 

Now run the app. You should see a Cast button in the app's navigation bar and when you click on it, it will list the Cast devices on your local network. Device discovery is managed automatically by the GCKCastContext . Select your Cast device and the sample receiver app will load on the Cast device. You can navigate between the browse activity and the local player activity and the Cast button state is kept in sync.

We haven't hooked up any support for media playback, so you can't play videos on the Cast device yet. Click on the Cast button to stop casting.

6. Casting video content

Illustration of an iPhone running CastVideos app, which shows details on a particular video ('Tears of Steel'). At the bottom is the mini player

We will extend the sample app to also play videos remotely on a Cast device. To do that we need to listen to the various events generated by the Cast framework.

Casting media

At a high level, if you want to play a media on a Cast device, the following needs to happen:

  1. Create a GCKMediaInformation object from the Cast SDK that models a media item.
  2. The user connects to the Cast device to launch your receiver application.
  3. Load the GCKMediaInformation object into your receiver and play the content.
  4. Track the media status.
  5. Send playback commands to the receiver based on user interactions.

Step 1 amounts to mapping one object to another; GCKMediaInformation is something that the Cast SDK understands and MediaItem is our app's encapsulation for a media item; we can easily map a MediaItem to a GCKMediaInformation . We have already done the Step 2 in the previous section. Step 3 is easy to do with the Cast SDK.

The sample app MediaViewController already distinguishes between local vs remote playback by using this enum:

  enum 
  
 PlaybackMode 
 : 
  
 Int 
  
 { 
  
 case 
  
 none 
  
 = 
  
 0 
  
 case 
  
 local 
  
 case 
  
 remote 
 } 
 private 
  
 var 
  
 playbackMode 
  
 = 
  
 PlaybackMode 
 . 
 none 
 

It's not important in this codelab for you to understand exactly how all the sample player logic works. It is important to understand that your app's media player will have to be modified to be aware of the two playback locations in a similar way.

At the moment the local player is always in the local playback state since it doesn't know anything about the Casting states yet. We need to update the UI based on state transitions that happen in the Cast framework. For example, if we start casting, we need to stop the local playback and disable some controls. Similarly, if we stop casting when we are in this view controller, we need to transition to local playback. To handle that we need to listen to the various events generated by the Cast framework.

Cast session management

For the Cast framework a Cast session combines the steps of connecting to a device, launching (or joining), connecting to a receiver application, and initializing a media control channel if appropriate. The media control channel is how the Cast framework sends and receives messages from the receiver media player.

The Cast session will be started automatically when user selects a device from the Cast button, and will be stopped automatically when user disconnects. Reconnecting to a receiver session due to networking issues is also automatically handled by the Cast framework.

Cast sessions are managed by the GCKSessionManager , which can be accessed via GCKCastContext.sharedInstance().sessionManager . The GCKSessionManagerListener callbacks can be used to monitor session events, such as creation, suspension, resumption, and termination.

First we need to register our session listener and initialize some variables:

  class 
  
 MediaViewController 
 : 
  
 UIViewController 
 , 
  
 GCKSessionManagerListener 
 , 
  
 GCKRemoteMediaClientListener 
 , 
  
 LocalPlayerViewDelegate 
 , 
  
 GCKRequestDelegate 
  
 { 
  
 ... 
  
 private 
  
 var 
  
 sessionManager 
 : 
  
 GCKSessionManager 
 ! 
  
 ... 
  
 required 
  
 init 
 ? 
 ( 
 coder 
 : 
  
 NSCoder 
 ) 
  
 { 
  
 super 
 . 
 init 
 ( 
 coder 
 : 
  
 coder 
 ) 
  
 sessionManager 
  
 = 
  
 GCKCastContext 
 . 
 sharedInstance 
 () 
 . 
 sessionManager 
  
 ... 
  
 } 
  
 override 
  
 func 
  
 viewWillAppear 
 ( 
 _ 
  
 animated 
 : 
  
 Bool 
 ) 
  
 { 
  
 ... 
  
 let 
  
 hasConnectedSession 
 : 
  
 Bool 
  
 = 
  
 ( 
 sessionManager 
 . 
 hasConnectedSession 
 ()) 
  
 if 
  
 hasConnectedSession 
 , 
  
 ( 
 playbackMode 
  
 != 
  
 . 
 remote 
 ) 
  
 { 
  
 populateMediaInfo 
 ( 
 false 
 , 
  
 playPosition 
 : 
  
 0 
 ) 
  
 switchToRemotePlayback 
 () 
  
 } 
  
 else 
  
 if 
  
 sessionManager 
 . 
 currentSession 
  
 == 
  
 nil 
 , 
  
 ( 
 playbackMode 
  
 != 
  
 . 
 local 
 ) 
  
 { 
  
 switchToLocalPlayback 
 () 
  
 } 
  
 sessionManager 
 . 
 add 
 ( 
 self 
 ) 
  
 ... 
  
 } 
  
 override 
  
 func 
  
 viewWillDisappear 
 ( 
 _ 
  
 animated 
 : 
  
 Bool 
 ) 
  
 { 
  
 ... 
  
 sessionManager 
 . 
 remove 
 ( 
 self 
 ) 
  
 sessionManager 
 . 
 currentCastSession 
 ? 
 . 
 remoteMediaClient 
 ? 
 . 
 remove 
 ( 
 self 
 ) 
  
 ... 
  
 super 
 . 
 viewWillDisappear 
 ( 
 animated 
 ) 
  
 } 
  
 func 
  
 switchToLocalPlayback 
 () 
  
 { 
  
 ... 
  
 sessionManager 
 . 
 currentCastSession 
 ? 
 . 
 remoteMediaClient 
 ? 
 . 
 remove 
 ( 
 self 
 ) 
  
 ... 
  
 } 
  
 func 
  
 switchToRemotePlayback 
 () 
  
 { 
  
 ... 
  
 sessionManager 
 . 
 currentCastSession 
 ? 
 . 
 remoteMediaClient 
 ? 
 . 
 add 
 ( 
 self 
 ) 
  
 ... 
  
 } 
  
 // 
  
 MARK 
 : 
  
 - 
  
 GCKSessionManagerListener 
  
 func 
  
 sessionManager 
 ( 
 _ 
 : 
  
 GCKSessionManager 
 , 
  
 didStart 
  
 session 
 : 
  
 GCKSession 
 ) 
  
 { 
  
 print 
 ( 
 "MediaViewController: sessionManager didStartSession \(session)" 
 ) 
  
 setQueueButtonVisible 
 ( 
 true 
 ) 
  
 switchToRemotePlayback 
 () 
  
 } 
  
 func 
  
 sessionManager 
 ( 
 _ 
 : 
  
 GCKSessionManager 
 , 
  
 didResumeSession 
  
 session 
 : 
  
 GCKSession 
 ) 
  
 { 
  
 print 
 ( 
 "MediaViewController: sessionManager didResumeSession \(session)" 
 ) 
  
 setQueueButtonVisible 
 ( 
 true 
 ) 
  
 switchToRemotePlayback 
 () 
  
 } 
  
 func 
  
 sessionManager 
 ( 
 _ 
 : 
  
 GCKSessionManager 
 , 
  
 didEnd 
  
 _ 
 : 
  
 GCKSession 
 , 
  
 withError 
  
 error 
 : 
  
 Error 
 ? 
 ) 
  
 { 
  
 print 
 ( 
 "session ended with error: \(String(describing: error))" 
 ) 
  
 let 
  
 message 
  
 = 
  
 "The Casting session has ended. 
 \n 
 \(String(describing: error))" 
  
 if 
  
 let 
  
 window 
  
 = 
  
 appDelegate 
 ? 
 . 
 window 
  
 { 
  
 Toast 
 . 
 displayMessage 
 ( 
 message 
 , 
  
 for 
 : 
  
 3 
 , 
  
 in 
 : 
  
 window 
 ) 
  
 } 
  
 setQueueButtonVisible 
 ( 
 false 
 ) 
  
 switchToLocalPlayback 
 () 
  
 } 
  
 func 
  
 sessionManager 
 ( 
 _ 
 : 
  
 GCKSessionManager 
 , 
  
 didFailToStartSessionWithError 
  
 error 
 : 
  
 Error 
 ? 
 ) 
  
 { 
  
 if 
  
 let 
  
 error 
  
 = 
  
 error 
  
 { 
  
 showAlert 
 ( 
 withTitle 
 : 
  
 "Failed to start a session" 
 , 
  
 message 
 : 
  
 error 
 . 
 localizedDescription 
 ) 
  
 } 
  
 setQueueButtonVisible 
 ( 
 false 
 ) 
  
 } 
  
 func 
  
 sessionManager 
 ( 
 _ 
 : 
  
 GCKSessionManager 
 , 
  
 didFailToResumeSession 
  
 _ 
 : 
  
 GCKSession 
 , 
  
 withError 
  
 _ 
 : 
  
 Error 
 ? 
 ) 
  
 { 
  
 if 
  
 let 
  
 window 
  
 = 
  
 UIApplication 
 . 
 shared 
 . 
 delegate 
 ? 
 . 
 window 
  
 { 
  
 Toast 
 . 
 displayMessage 
 ( 
 "The Casting session could not be resumed." 
 , 
  
 for 
 : 
  
 3 
 , 
  
 in 
 : 
  
 window 
 ) 
  
 } 
  
 setQueueButtonVisible 
 ( 
 false 
 ) 
  
 switchToLocalPlayback 
 () 
  
 } 
  
 ... 
 } 
 

In MediaViewController , we are interested to be informed when we get connected or disconnected from the Cast device so we can switch to or from the local player. Note that connectivity can be disrupted not only by the instance of your application running on your mobile device, but it can also be disrupted by another instance of your (or another) application running on a different mobile device.

The currently active session is accessible as GCKCastContext.sharedInstance().sessionManager.currentCastSession . Sessions are created and torn down automatically in response to user gestures from the Cast dialogs.

Loading media

In the Cast SDK, the GCKRemoteMediaClient provides a set of convenient APIs for managing the remote media playback on the receiver. For a GCKCastSession that supports media playback, an instance of GCKRemoteMediaClient will be created automatically by the SDK. It can be accessed as the remoteMediaClient property of the GCKCastSession instance.

Add the following code to MediaViewController.swift to load the currently selected video on the receiver:

  @objc 
 ( 
 MediaViewController 
 ) 
 class 
  
 MediaViewController 
 : 
  
 UIViewController 
 , 
  
 GCKSessionManagerListener 
 , 
  
 GCKRemoteMediaClientListener 
 , 
  
 LocalPlayerViewDelegate 
 , 
  
 GCKRequestDelegate 
  
 { 
  
 ... 
  
 @objc 
  
 func 
  
 playSelectedItemRemotely 
 () 
  
 { 
  
 loadSelectedItem 
 ( 
 byAppending 
 : 
  
 false 
 ) 
  
 } 
  
 /** 
 * Loads the currently selected item in the current cast media session. 
 * @param appending If YES, the item is appended to the current queue if there 
 * is one. If NO, or if 
 * there is no queue, a new queue containing only the selected item is created. 
 */ 
  
 func 
  
 loadSelectedItem 
 ( 
 byAppending 
  
 appending 
 : 
  
 Bool 
 ) 
  
 { 
  
 print 
 ( 
 "enqueue item \(String(describing: mediaInfo))" 
 ) 
  
 if 
  
 let 
  
 remoteMediaClient 
  
 = 
  
 sessionManager 
 . 
 currentCastSession 
 ? 
 . 
 remoteMediaClient 
  
 { 
  
 let 
  
 mediaQueueItemBuilder 
  
 = 
  
 GCKMediaQueueItemBuilder 
 () 
  
 mediaQueueItemBuilder 
 . 
 mediaInformation 
  
 = 
  
 mediaInfo 
  
 mediaQueueItemBuilder 
 . 
 autoplay 
  
 = 
  
 true 
  
 mediaQueueItemBuilder 
 . 
 preloadTime 
  
 = 
  
 TimeInterval 
 ( 
 UserDefaults 
 . 
 standard 
 . 
 integer 
 ( 
 forKey 
 : 
  
 kPrefPreloadTime 
 )) 
  
 let 
  
 mediaQueueItem 
  
 = 
  
 mediaQueueItemBuilder 
 . 
 build 
 () 
  
 if 
  
 appending 
  
 { 
  
 let 
  
 request 
  
 = 
  
 remoteMediaClient 
 . 
 queueInsert 
 ( 
 mediaQueueItem 
 , 
  
 beforeItemWithID 
 : 
  
 kGCKMediaQueueInvalidItemID 
 ) 
  
 request 
 . 
 delegate 
  
 = 
  
 self 
  
 } 
  
 else 
  
 { 
  
 let 
  
 queueDataBuilder 
  
 = 
  
 GCKMediaQueueDataBuilder 
 ( 
 queueType 
 : 
  
 . 
 generic 
 ) 
  
 queueDataBuilder 
 . 
 items 
  
 = 
  
 [ 
 mediaQueueItem 
 ] 
  
 queueDataBuilder 
 . 
 repeatMode 
  
 = 
  
 remoteMediaClient 
 . 
 mediaStatus 
 ? 
 . 
 queueRepeatMode 
  
 ?? 
  
 . 
 off 
  
 let 
  
 mediaLoadRequestDataBuilder 
  
 = 
  
 GCKMediaLoadRequestDataBuilder 
 () 
  
 mediaLoadRequestDataBuilder 
 . 
 mediaInformation 
  
 = 
  
 mediaInfo 
  
 mediaLoadRequestDataBuilder 
 . 
 queueData 
  
 = 
  
 queueDataBuilder 
 . 
 build 
 () 
  
 let 
  
 request 
  
 = 
  
 remoteMediaClient 
 . 
 loadMedia 
 ( 
 with 
 : 
  
 mediaLoadRequestDataBuilder 
 . 
 build 
 ()) 
  
 request 
 . 
 delegate 
  
 = 
  
 self 
  
 } 
  
 } 
  
 } 
  
 ... 
 } 
 

Now update various existing methods to use the Cast Session logic to support remote playback:

  required 
  
 init 
 ? 
 ( 
 coder 
 : 
  
 NSCoder 
 ) 
  
 { 
  
 super 
 . 
 init 
 ( 
 coder 
 : 
  
 coder 
 ) 
  
 ... 
  
 castMediaController 
  
 = 
  
 GCKUIMediaController 
 () 
  
 ... 
 } 
 func 
  
 switchToLocalPlayback 
 () 
  
 { 
  
 print 
 ( 
 "switchToLocalPlayback" 
 ) 
  
 if 
  
 playbackMode 
  
 == 
  
 . 
 local 
  
 { 
  
 return 
  
 } 
  
 setQueueButtonVisible 
 ( 
 false 
 ) 
  
 var 
  
 playPosition 
 : 
  
 TimeInterval 
  
 = 
  
 0 
  
 var 
  
 paused 
 : 
  
 Bool 
  
 = 
  
 false 
  
 var 
  
 ended 
 : 
  
 Bool 
  
 = 
  
 false 
  
 if 
  
 playbackMode 
  
 == 
  
 . 
 remote 
  
 { 
  
 playPosition 
  
 = 
  
 castMediaController 
 . 
 lastKnownStreamPosition 
  
 paused 
  
 = 
  
 ( 
 castMediaController 
 . 
 lastKnownPlayerState 
  
 == 
  
 . 
 paused 
 ) 
  
 ended 
  
 = 
  
 ( 
 castMediaController 
 . 
 lastKnownPlayerState 
  
 == 
  
 . 
 idle 
 ) 
  
 print 
 ( 
 "last player state: \(castMediaController.lastKnownPlayerState), ended: \(ended)" 
 ) 
  
 } 
  
 populateMediaInfo 
 (( 
 ! 
 paused 
 && 
 ! 
 ended 
 ), 
  
 playPosition 
 : 
  
 playPosition 
 ) 
  
 sessionManager 
 . 
 currentCastSession 
 ? 
 . 
 remoteMediaClient 
 ? 
 . 
 remove 
 ( 
 self 
 ) 
  
 playbackMode 
  
 = 
  
 . 
 local 
 } 
 func 
  
 switchToRemotePlayback 
 () 
  
 { 
  
 print 
 ( 
 "switchToRemotePlayback; mediaInfo is \(String(describing: mediaInfo))" 
 ) 
  
 if 
  
 playbackMode 
  
 == 
  
 . 
 remote 
  
 { 
  
 return 
  
 } 
  
 // 
  
 If 
  
 we 
  
 were 
  
 playing 
  
 locally 
 , 
  
 load 
  
 the 
  
 local 
  
 media 
  
 on 
  
 the 
  
 remote 
  
 player 
  
 if 
  
 playbackMode 
  
 == 
  
 . 
 local 
 , 
  
 ( 
 _localPlayerView 
 . 
 playerState 
  
 != 
  
 . 
 stopped 
 ), 
  
 ( 
 mediaInfo 
  
 != 
  
 nil 
 ) 
  
 { 
  
 print 
 ( 
 "loading media: \(String(describing: mediaInfo))" 
 ) 
  
 let 
  
 paused 
 : 
  
 Bool 
  
 = 
  
 ( 
 _localPlayerView 
 . 
 playerState 
  
 == 
  
 . 
 paused 
 ) 
  
 let 
  
 mediaQueueItemBuilder 
  
 = 
  
 GCKMediaQueueItemBuilder 
 () 
  
 mediaQueueItemBuilder 
 . 
 mediaInformation 
  
 = 
  
 mediaInfo 
  
 mediaQueueItemBuilder 
 . 
 autoplay 
  
 = 
  
 ! 
 paused 
  
 mediaQueueItemBuilder 
 . 
 preloadTime 
  
 = 
  
 TimeInterval 
 ( 
 UserDefaults 
 . 
 standard 
 . 
 integer 
 ( 
 forKey 
 : 
  
 kPrefPreloadTime 
 )) 
  
 mediaQueueItemBuilder 
 . 
 startTime 
  
 = 
  
 _localPlayerView 
 . 
 streamPosition 
  
 ?? 
  
 0 
  
 let 
  
 mediaQueueItem 
  
 = 
  
 mediaQueueItemBuilder 
 . 
 build 
 () 
  
 let 
  
 queueDataBuilder 
  
 = 
  
 GCKMediaQueueDataBuilder 
 ( 
 queueType 
 : 
  
 . 
 generic 
 ) 
  
 queueDataBuilder 
 . 
 items 
  
 = 
  
 [ 
 mediaQueueItem 
 ] 
  
 queueDataBuilder 
 . 
 repeatMode 
  
 = 
  
 . 
 off 
  
 let 
  
 mediaLoadRequestDataBuilder 
  
 = 
  
 GCKMediaLoadRequestDataBuilder 
 () 
  
 mediaLoadRequestDataBuilder 
 . 
 queueData 
  
 = 
  
 queueDataBuilder 
 . 
 build 
 () 
  
 let 
  
 request 
  
 = 
  
 sessionManager 
 . 
 currentCastSession 
 ? 
 . 
 remoteMediaClient 
 ? 
 . 
 loadMedia 
 ( 
 with 
 : 
  
 mediaLoadRequestDataBuilder 
 . 
 build 
 ()) 
  
 request 
 ? 
 . 
 delegate 
  
 = 
  
 self 
  
 } 
  
 _localPlayerView 
 . 
 stop 
 () 
  
 _localPlayerView 
 . 
 showSplashScreen 
 () 
  
 setQueueButtonVisible 
 ( 
 true 
 ) 
  
 sessionManager 
 . 
 currentCastSession 
 ? 
 . 
 remoteMediaClient 
 ? 
 . 
 add 
 ( 
 self 
 ) 
  
 playbackMode 
  
 = 
  
 . 
 remote 
 } 
 /* 
  
 Play 
  
 has 
  
 been 
  
 pressed 
  
 in 
  
 the 
  
 LocalPlayerView 
 . 
  
 */ 
 func 
  
 continueAfterPlayButtonClicked 
 () 
  
 - 
>  
 Bool 
  
 { 
  
 let 
  
 hasConnectedCastSession 
  
 = 
  
 sessionManager 
 . 
 hasConnectedCastSession 
  
 if 
  
 mediaInfo 
  
 != 
  
 nil 
 , 
  
 hasConnectedCastSession 
 () 
  
 { 
  
 // 
  
 Display 
  
 an 
  
 alert 
  
 box 
  
 to 
  
 allow 
  
 the 
  
 user 
  
 to 
  
 add 
  
 to 
  
 queue 
  
 or 
  
 play 
  
 // 
  
 immediately 
 . 
  
 if 
  
 actionSheet 
  
 == 
  
 nil 
  
 { 
  
 actionSheet 
  
 = 
  
 ActionSheet 
 ( 
 title 
 : 
  
 "Play Item" 
 , 
  
 message 
 : 
  
 "Select an action" 
 , 
  
 cancelButtonText 
 : 
  
 "Cancel" 
 ) 
  
 actionSheet 
 ? 
 . 
 addAction 
 ( 
 withTitle 
 : 
  
 "Play Now" 
 , 
  
 target 
 : 
  
 self 
 , 
  
 selector 
 : 
  
 #selector(playSelectedItemRemotely)) 
  
 } 
  
 actionSheet 
 ? 
 . 
 present 
 ( 
 in 
 : 
  
 self 
 , 
  
 sourceView 
 : 
  
 _localPlayerView 
 ) 
  
 return 
  
 false 
  
 } 
  
 return 
  
 true 
 } 
 

Now, run the app on your mobile device. Connect to your Cast device and start playing a video. You should see the video playing on the receiver.

7. Mini controller

The Cast Design Checklist requires that all Cast apps provide mini controller to appear when the user navigates away from the current content page. The mini controller provide instant access and a visible reminder for the current Cast session.

Illustration of the bottom portion of an iPhone running the CastVideos app, focusing on the mini controller

The Cast SDK provides a control bar, GCKUIMiniMediaControlsViewController , which can be added to the scenes in which you want to show the persistent controls.

For the sample app, we are going to use the GCKUICastContainerViewController which wraps another view controller and adds a GCKUIMiniMediaControlsViewController at the bottom.

Modify the AppDelegate.swift file and add the following code for if useCastContainerViewController condition in the following method:

  func 
  
 application 
 ( 
 _ 
 : 
  
 UIApplication 
 , 
  
 didFinishLaunchingWithOptions 
  
 _ 
 : 
  
 [ 
 UIApplication 
 . 
 LaunchOptionsKey 
 : 
  
 Any 
 ] 
 ? 
 ) 
  
 - 
>  
 Bool 
  
 { 
  
 ... 
  
 let 
  
 appStoryboard 
  
 = 
  
 UIStoryboard 
 ( 
 name 
 : 
  
 "Main" 
 , 
  
 bundle 
 : 
  
 nil 
 ) 
  
 guard 
  
 let 
  
 navigationController 
  
 = 
  
 appStoryboard 
 . 
 instantiateViewController 
 ( 
 withIdentifier 
 : 
  
 "MainNavigation" 
 ) 
  
 as 
 ? 
  
 UINavigationController 
  
 else 
  
 { 
  
 return 
  
 false 
  
 } 
  
 let 
  
 castContainerVC 
  
 = 
  
 GCKCastContext 
 . 
 sharedInstance 
 () 
 . 
 createCastContainerController 
 ( 
 for 
 : 
  
 navigationController 
 ) 
  
 as 
  
 GCKUICastContainerViewController 
  
 castContainerVC 
 . 
 miniMediaControlsItemEnabled 
  
 = 
  
 true 
  
 window 
  
 = 
  
 UIWindow 
 ( 
 frame 
 : 
  
 UIScreen 
 . 
 main 
 . 
 bounds 
 ) 
  
 window 
 ? 
 . 
 rootViewController 
  
 = 
  
 castContainerVC 
  
 window 
 ? 
 . 
 makeKeyAndVisible 
 () 
  
 ... 
 } 
 

Add this property and setter/getter to control the visibility of the mini controller (we will use these in a later section):

  var 
  
 isCastControlBarsEnabled 
 : 
  
 Bool 
  
 { 
  
 get 
  
 { 
  
 if 
  
 useCastContainerViewController 
  
 { 
  
 let 
  
 castContainerVC 
  
 = 
  
 ( 
 window 
 ? 
 . 
 rootViewController 
  
 as 
 ? 
  
 GCKUICastContainerViewController 
 ) 
  
 return 
  
 castContainerVC 
 !. 
 miniMediaControlsItemEnabled 
  
 } 
  
 else 
  
 { 
  
 let 
  
 rootContainerVC 
  
 = 
  
 ( 
 window 
 ? 
 . 
 rootViewController 
  
 as 
 ? 
  
 RootContainerViewController 
 ) 
  
 return 
  
 rootContainerVC 
 !. 
 miniMediaControlsViewEnabled 
  
 } 
  
 } 
  
 set 
 ( 
 notificationsEnabled 
 ) 
  
 { 
  
 if 
  
 useCastContainerViewController 
  
 { 
  
 var 
  
 castContainerVC 
 : 
  
 GCKUICastContainerViewController 
 ? 
  
 castContainerVC 
  
 = 
  
 ( 
 window 
 ? 
 . 
 rootViewController 
  
 as 
 ? 
  
 GCKUICastContainerViewController 
 ) 
  
 castContainerVC 
 ? 
 . 
 miniMediaControlsItemEnabled 
  
 = 
  
 notificationsEnabled 
  
 } 
  
 else 
  
 { 
  
 var 
  
 rootContainerVC 
 : 
  
 RootContainerViewController 
 ? 
  
 rootContainerVC 
  
 = 
  
 ( 
 window 
 ? 
 . 
 rootViewController 
  
 as 
 ? 
  
 RootContainerViewController 
 ) 
  
 rootContainerVC 
 ? 
 . 
 miniMediaControlsViewEnabled 
  
 = 
  
 notificationsEnabled 
  
 } 
  
 } 
  
 } 
 

Run the app and cast a video. When playback starts on the receiver you should see the mini controller appear at the bottom of each scene. You can control the remote playback using the mini controller. If you navigate between the browse activity and the local player activity, the mini controller state should stay in sync with the receiver media playback status.

8. Introductory overlay

The Google Cast design checklist requires a sender app to introduce the Cast button to existing users to let them know that the sender app now supports Casting and also helps users new to Google Cast.

Illustration of an iPhone running the CastVideos app with the Cast button overlay, highlighting the Cast button and displaying the message 'Touch to cast media to your TV and Speakers'

The GCKCastContext class has a method, presentCastInstructionsViewControllerOnce , that can be used to highlight the Cast button when it is first shown to users. Add the following code to MediaViewController.swift and MediaTableViewController.swift :

  override 
  
 func 
  
 viewDidLoad 
 () 
  
 { 
  
 ... 
  
 NotificationCenter 
 . 
 default 
 . 
 addObserver 
 ( 
 self 
 , 
  
 selector 
 : 
  
 #selector(castDeviceDidChange), 
  
 name 
 : 
  
 NSNotification 
 . 
 Name 
 . 
 gckCastStateDidChange 
 , 
  
 object 
 : 
  
 GCKCastContext 
 . 
 sharedInstance 
 ()) 
 } 
 @ 
 objc 
  
 func 
  
 castDeviceDidChange 
 ( 
 _ 
 : 
  
 Notification 
 ) 
  
 { 
  
 if 
  
 GCKCastContext 
 . 
 sharedInstance 
 () 
 . 
 castState 
  
 != 
  
 . 
 noDevicesAvailable 
  
 { 
  
 // 
  
 You 
  
 can 
  
 present 
  
 the 
  
 instructions 
  
 on 
  
 how 
  
 to 
  
 use 
  
 Google 
  
 Cast 
  
 on 
  
 // 
  
 the 
  
 first 
  
 time 
  
 the 
  
 user 
  
 uses 
  
 you 
  
 app 
  
 GCKCastContext 
 . 
 sharedInstance 
 () 
 . 
 presentCastInstructionsViewControllerOnce 
 ( 
 with 
 : 
  
 castButton 
 ) 
  
 } 
 } 
 

Run the app on your mobile device and you should see the introductory overlay.

9. Expanded controller

The Google Cast design checklist requires a sender app to provide expanded controller for the media being cast. The expanded controller is a full screen version of the mini controller.

Illustration of an iPhone running the CastVideos app playing a video with the expanded controller appearing at the bottom

The expanded controller is a full screen view which offers full control of the remote media playback. This view should allow a casting app to manage every manageable aspect of a cast session, with the exception of receiver volume control and session lifecycle (connect/stop casting). It also provides all the status information about the media session (artwork, title, subtitle, and so forth).

The functionality of this view is implemented by the GCKUIExpandedMediaControlsViewController class.

The first thing you have to do is enable the default expanded controller in the cast context. Modify AppDelegate.swift to enable the default expanded controller:

  import 
  
 GoogleCast 
 @UIApplicationMain 
 class 
  
 AppDelegate 
 : 
 UIResponder 
 , 
 UIApplicationDelegate 
 { 
 ... 
 func 
 application 
 ( 
 _ 
 : 
 UIApplication 
 , 
 didFinishLaunchingWithOptions 
 _ 
 : 
 [ 
 UIApplication 
 . 
 LaunchOptionsKey 
 : 
 Any 
 ] 
 ? 
 ) 
 - 
> Bool 
 { 
 ... 
 // 
 Add 
 after 
 the 
 setShareInstanceWith 
 ( 
 options 
 ) 
 is 
 set 
 . 
 GCKCastContext 
 . 
 sharedInstance 
 () 
 . 
 useDefaultExpandedMediaControls 
 = 
 true 
 ... 
 } 
 ... 
 } 
 

Add the following code to MediaViewController.swift to load the expanded controller when the user starts to cast a video:

  @ 
 objc 
  
 func 
  
 playSelectedItemRemotely 
 () 
  
 { 
  
 ... 
  
 appDelegate 
 ? 
 . 
 isCastControlBarsEnabled 
  
 = 
  
 false 
  
 GCKCastContext 
 . 
 sharedInstance 
 () 
 . 
 presentDefaultExpandedMediaControls 
 () 
 } 
 

The expanded controller will also be launched automatically when the user taps the mini controller.

Run the app and cast a video. You should see the expanded controller. Navigate back to the list of videos and when you click on the mini controller, the expanded controller will be loaded again.

10. Add Cast Connect support

Cast Connect library allows existing sender applications to communicate with Android TV applications via the Cast protocol. Cast Connect builds on top of the Cast infrastructure, with your Android TV app acting as a receiver.

Dependencies

In your Podfile , make sure the google-cast-sdk is pointed to 4.4.8 or higher as listed below. If you made a modification to the file, run pod update from the console to sync the change with your project.

 pod 'google-cast-sdk', '>=4.4.8' 

GCKLaunchOptions

In order to launch the Android TV application, also referred to as the Android Receiver, we need to set the androidReceiverCompatible flag to true in the GCKLaunchOptions object. This GCKLaunchOptions object dictates how the receiver is launched and is passed to the GCKCastOptions which are set in the shared instance using GCKCastContext.setSharedInstanceWith .

Add the following lines to your AppDelegate.swift :

 let options = GCKCastOptions(discoveryCriteria:
                          GCKDiscoveryCriteria(applicationID: kReceiverAppID))
...
/** Following code enables CastConnect */
let launchOptions = GCKLaunchOptions()
launchOptions.androidReceiverCompatible = true
options.launchOptions = launchOptions

GCKCastContext.setSharedInstanceWith(options) 

Set Launch Credentials

On the sender side, you can specify GCKCredentialsData to represent who is joining the session. The credentials is a string which can be user-defined, as long as your ATV app can understand it. The GCKCredentialsData is only passed to your Android TV app during launch or join time. If you set it again while you are connected, it won't be passed to your Android TV app.

In order to set Launch Credentials GCKCredentialsData needs to be defined anytime after the GCKLaunchOptions are set. To demonstrate this, let's add logic for the Credsbutton to set credentials to be passed along when the session is established. Add the following code to your MediaTableViewController.swift :

  class 
  
 MediaTableViewController 
 : 
  
 UITableViewController 
 , 
  
 GCKSessionManagerListener 
 , 
  
 MediaListModelDelegate 
 , 
  
 GCKRequestDelegate 
  
 { 
  
 ... 
  
 private 
  
 var 
  
 credentials 
 : 
  
 String 
 ? 
  
 = 
  
 nil 
  
 ... 
  
 override 
  
 func 
  
 viewDidLoad 
 () 
  
 { 
  
 ... 
  
 navigationItem 
 . 
 leftBarButtonItem 
  
 = 
  
 UIBarButtonItem 
 ( 
 title 
 : 
  
 "Creds" 
 , 
  
 style 
 : 
  
 . 
 plain 
 , 
  
 target 
 : 
  
 self 
 , 
  
 action 
 : 
  
 #selector(toggleLaunchCreds)) 
  
 ... 
  
 setLaunchCreds 
 () 
  
 } 
  
 ... 
  
 @ 
 objc 
  
 func 
  
 toggleLaunchCreds 
 ( 
 _ 
 : 
  
 Any 
 ){ 
  
 if 
  
 ( 
 credentials 
  
 == 
  
 nil 
 ) 
  
 { 
  
 credentials 
  
 = 
  
 "{ 
 \" 
 userId 
 \" 
 : 
 \" 
 id123 
 \" 
 }" 
  
 } 
  
 else 
  
 { 
  
 credentials 
  
 = 
  
 nil 
  
 } 
  
 Toast 
 . 
 displayMessage 
 ( 
 "Launch Credentials: " 
 + 
 ( 
 credentials 
  
 ?? 
  
 "Null" 
 ), 
  
 for 
 : 
  
 3 
 , 
  
 in 
 : 
  
 appDelegate 
 ? 
 . 
 window 
 ) 
  
 print 
 ( 
 "Credentials set: " 
 + 
 ( 
 credentials 
  
 ?? 
  
 "Null" 
 )) 
  
 setLaunchCreds 
 () 
  
 } 
  
 ... 
  
 func 
  
 setLaunchCreds 
 () 
  
 { 
  
 GCKCastContext 
 . 
 sharedInstance 
 () 
  
 . 
 setLaunch 
 ( 
 GCKCredentialsData 
 ( 
 credentials 
 : 
  
 credentials 
 )) 
  
 } 
 } 
 

Set Credentials on Load Request

In order to handle credentials on both your Web and Android TV Receiver apps, add the following code in your MediaTableViewController.swift class under loadSelectedItem function:

 let mediaLoadRequestDataBuilder = GCKMediaLoadRequestDataBuilder()
...
mediaLoadRequestDataBuilder.credentials = credentials
... 

Depending on the receiver app your sender is casting to, the SDK would automatically apply the above credentials to the ongoing session.

Testing Cast Connect

Steps to install the Android TV APK on Chromecast with Google TV

  1. Find the IP Address of your Android TV device. Usually, it's available under Settings > Network & Internet > (Network name your device is connected to). On the right hand it will show the details and your device's IP on the network.
  2. Use the IP address for your device to connect to it via ADB using the terminal:
$ adb connect <device_ip_address>:5555
  1. From your terminal window, navigate into the top level folder for the codelab samples that you downloaded at the start of this codelab. For example:
$ cd Desktop/ios_codelab_src
  1. Install the .apk file in this folder to your Android TV by running:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
  1. You should now be able to see an app by the name of Cast Videosin the Your Appsmenu on your Android TV device.
  2. Once done, build and run the app on an emulator or a mobile device. On establishing a cast session with your Android TV device, it should now launch the Android Receiver application on your Android TV. Playing a video from your iOS mobile sender, should launch the video in the Android Receiver and allow you to control playback using the remote for your Android TV device.

11. Customize Cast widgets

Initialization

Start with the App-Done folder. Add the following to the applicationDidFinishLaunchingWithOptions method in your AppDelegate.swift file.

  func 
  
 application 
 ( 
 _ 
 : 
  
 UIApplication 
 , 
  
 didFinishLaunchingWithOptions 
  
 _ 
 : 
  
 [ 
 UIApplication 
 . 
 LaunchOptionsKey 
 : 
  
 Any 
 ] 
 ? 
 ) 
  
 - 
>  
 Bool 
  
 { 
  
 ... 
  
 let 
  
 styler 
  
 = 
  
 GCKUIStyle 
 . 
 sharedInstance 
 () 
  
 ... 
 } 
 

Once you are done applying one or more customizations as mentioned in the rest of this codelab, commit the styles by calling the code below

 styler.apply() 

Customizing Cast views

You can customize all views that the Cast Application Framework manages by having default styling guidelines across views. As an example, let's change the icon tint color.

 styler.castViews.iconTintColor = .lightGray 

You can override defaults on a per-screen basis if required. For example, to override the lightGrayColor for the icon tint color just for the expanded media controller.

 styler.castViews.mediaControl.expandedController.iconTintColor = .green 

Changing colors

You can customize the background color for all views (or individually for each view). The following code sets the background color to blue for all your Cast Application Framework provided views.

 styler.castViews.backgroundColor = .blue
styler.castViews.mediaControl.miniController.backgroundColor = .yellow 

Changing fonts

You can customize fonts for different labels seen within cast views. Let's set all fonts to ‘Courier-Oblique' for illustration purposes.

 styler.castViews.headingTextFont = UIFont.init(name: "Courier-Oblique", size: 16) ?? UIFont.systemFont(ofSize: 16)
styler.castViews.mediaControl.headingTextFont = UIFont.init(name: "Courier-Oblique", size: 6) ?? UIFont.systemFont(ofSize: 6) 

Changing default button images

Add your own custom images to the project, and assign the images to your buttons to style them.

 let muteOnImage = UIImage.init(named: "yourImage.png")
if let muteOnImage = muteOnImage {
  styler.castViews.muteOnImage = muteOnImage
} 

Changing the Cast button theme

You can also theme Cast Widgets using the UIAppearance Protocol. The following code themes the GCKUICastButton on all the views it appears:

 GCKUICastButton.appearance().tintColor = UIColor.gray 

12. Congratulations

You now know how to Cast-enable a video app using the Cast SDK widgets on iOS.

For more details, see the iOS Sender developer guide.

Create a Mobile Website
View Site in Mobile | Classic
Share by: