Add Advanced CAF Sender Features to your iOS App

Style the Widgets

You can customize Cast widgets by setting the colors, styling the buttons, text and thumbnail appearance, and by choosing the types of buttons to display.

Customize the Widgets

The Cast framework widgets supports the Apple UIAppearance Protocol in UIKit to change the appearance of the widgets across your app, such as the position or border of a button. Use this protocol to style the Cast framework widgets to match an existing apps styling.

Choose Controller Buttons

Both the expanded controller class (GCKUIExpandedMediaControlsViewController) and the mini controller class (GCKUIMiniMediaControlsViewController) contain a button bar, and clients can configure which buttons are presented on those bars. This is achieved by both classes conforming to GCKUIMediaButtonBarProtocol.

The mini controller bar has 3 configurable slots for buttons:

  1     2     3

The expanded controller bar has a permanent play/pause toggle button in the middle of the bar plus 4 configurable slots:

  1     2     BUTTON      3     4

Your app can get a reference to the expanded controller with the GCKCastContext::defaultExpandedMediaControlsViewController property and can get a reference to the mini controller if using GCKCastContext::createMiniMediaControlsViewController(void).

Each slot can contain either a framework button, a custom button, or be empty. The list of framework control buttons are defined as:

Button Type Description
GCKUIMediaButtonTypeNone Do not place a button in this slot
GCKUIMediaButtonTypeCustom Custom button
GCKUIMediaButtonTypePlayPauseToggle Toggles between playback and pause
GCKUIMediaButtonTypeSkipPrevious Skips to the previous item in the queue
GCKUIMediaButtonTypeSkipNext Skips to the next item in the queue
GCKUIMediaButtonTypeRewind30Seconds Rewinds the playback by 30 seconds
GCKUIMediaButtonTypeForward30Seconds Skips forward the playback by 30 seconds
GCKUIMediaButtonTypeMuteToggle Mutes and unmutes the remote receiver
GCKUIMediaButtonTypeClosedCaptions Opens a dialog to select text and audio tracks

Detailed descriptions of what each button does can be found in GCKUIMediaButtonBarProtocol.h

Add a button as follows:

  • To add a framework button to a bar requires only a call to -[setButtonType:atIndex:].

  • To add a custom button to a bar, your app must call -[setButtonType:atIndex:] with buttonType set to GCKUIMediaButtonTypeCustom, and then call -[setCustomButton:atIndex:] passing the UIButton with the same index.

How Volume Control Works

The Cast framework automatically manages the volume for the sender app. The framework automatically synchronizes with the receiver volume for the supplied UI widgets. To sync a slider provided by the app, use GCKUIDeviceVolumeController.

Variable Playback Rate

Your app can display and change the playback rate for the current media item. Set the rate using -[setPlaybackRate:] or -[setPlaybackRate:customData:] Access the playback rate controller at playbackRateController. Display the current playback rate at GCKUIPlaybackRateController.

Sample Code

The following two files implement GCKUIPlaybackRateController which controls the playback rate using a segmented control that has "normal", "half speed", and "double speed" buttons.

FILE: SegmentedButtonPlaybackRateController.h

#import <GoogleCast/GoogleCast.h>
#import <UIKit/UIKit.h>

 * An implementation of GCKUIPlaybackRateController that controls playback rate
 * using a segmented control that has "normal", "half speed", and "double speed" 
 * buttons.
@interface SegmentedButtonPlaybackRateController : GCKUIPlaybackRateController

 * Designated initializer.
 * @param segmentedControl The segmented control for changing/displaying the 
 * playback rate.
- (instancetype)initWithSegmentedControl:(UISegmentedControl *)segmentedControl;


FILE: SegmentedButtonPlaybackRateController.m

#import "SegmentedButtonPlaybackRateController.h"

@interface SegmentedButtonPlaybackRateController () {
  UISegmentedControl *_segmentedControl;


static const NSInteger kSegmentNormal = 0;
static const NSInteger kSegmentHalfSpeed = 1;
static const NSInteger kSegmentDoubleSpeed = 2;

@implementation SegmentedButtonPlaybackRateController

- (instancetype)initWithSegmentedControl:(UISegmentedControl *)segmentedControl {
  if (self = [super init]) {
    _segmentedControl = segmentedControl;
    [_segmentedControl addTarget:self
  return self;

- (void)setPlaybackRate:(float)playbackRate {
  [super setPlaybackRate:playbackRate];

  NSInteger buttonIndex = 0;

  // Map the playback rate to one of our three supported speeds.
  if (playbackRate == 1.0) {
    buttonIndex = kSegmentNormal;
  } else if (playbackRate < 1.0) {
    buttonIndex = kSegmentHalfSpeed;
  } else {
    buttonIndex = kSegmentDoubleSpeed;

  _segmentedControl.selectedSegmentIndex = buttonIndex;

- (void)setInputEnabled:(BOOL)inputEnabled {
  _segmentedControl.enabled = inputEnabled;
  [super setInputEnabled:inputEnabled];

- (void)segmentedControlTapped:(id)sender {
  float playbackRate;

  switch (_segmentedControl.selectedSegmentIndex) {
    case kSegmentHalfSpeed:
      playbackRate = 0.5;

    case kSegmentDoubleSpeed:
      playbackRate = 2.0;

    case kSegmentNormal:
      playbackRate = 1.0;

  self.playbackRate = playbackRate;


Using Media Tracks

A media track can be an audio or video stream object, or a text object (subtitle or caption).

A GCKMediaTrack object represents a track. It consists of a unique numeric identifier and other attributes such as a content ID and title. A GCKMediaTrack instance can be created as follows:

GCKMediaTrack *captionsTrack =
      [[GCKMediaTrack alloc] initWithIdentifier:1
                                           name:@"English Captions"

A media item can have multiple tracks; for example, it can have multiple subtitles (each for a different language) or multiple alternative audio streams (for different languages). GCKMediaInformation is the class that represents a media item. To associate a collection of GCKMediaTrack objects with a media item, your app should update its mediaTracks property. Your app needs to make his association before it loads the media to the receiver, as in the following code.

NSArray *tracks = @[captionsTrack];

GCKMediaInformation *mediaInformation =
    [[GCKMediaInformation alloc] initWithContentID:@"https://some-url/"

Activate one or more tracks that were associated with the media item (after the media is loaded) by calling GCKRemoteMediaClient setActiveTrackIDs and passing the IDs of the tracks to be activated. For example, the following code activates the captions track created above.

[_remoteMediaClient setActiveTrackIDs:@[@1]];

To deactivate a track on the current media item, call -[setActiveTrackIDs:] on GCKRemoteMediaClient with an empty array or nil. The following code disables the captions track.

[_remoteMediaClient setActiveTrackIDs: @[]];

Styling Text Tracks

The GCKMediaTextTrackStyle class encapsulates the style information of a text track. A track style can be applied to the currently playing media item by calling GCKRemoteMediaClientsetTextTrackStyle. The track style created in the code below turns text red (FF) at 50% opacity (80) and sets a serif font.

GCKMediaTextTrackStyle *textTrackStyle = [GCKMediaTextTrackStyle createDefault];
[textTrackStyle setForegroundColor:[[GCKColor alloc] initWithCSSString:@"#FF000080"]];
[textTrackStyle setFontFamily:@"serif"];
_styleChangeRequest = [_mediaControlChannel setTextTrackStyle:textTrackStyle];
_styleChangeRequest.delegate = self;

You can use the returned GCKRequest object for tracking this request.

#pragma mark - GCKRequestDelegate

- (void)requestDidComplete:(GCKRequest *)request {
  if (request == _styleChangeRequest) {
    NSLog(@"Style update completed.");
    _styleChangeRequest = nil;

See Status updates below for more information. Apps should allow users to update the style for text tracks, either using the settings provided by the system or by the app itself. There is a default style provided (in iOS 7 and later), which can be retrieved via the static method +[GCKMediaTextTrackStyle createDefault]. The following text track style elements can be changed:

  • Foreground (text) color and opacity
  • Background color and opacity
  • Edge type
  • Edge Color
  • Font Scale
  • Font Family
  • Font Style

Receive Status Updates

When multiple senders are connected to the same receiver, it is important for each sender to be aware of the changes on the receiver even if those changes were initiated from other senders.

To this end, your app should register a GCKRemoteMediaClientListener. If the GCKMediaTextTrackStyle of the current media changes, then all of the connected senders will be notified through both the mediaControlChannelDidUpdateMetadata: and the mediaControlChannelDidUpdateStatus: callbacks. In this case, the receiver classes do not verify whether the new style is different from the previous one and notifies all the connected senders regardless. If, however, the list of active tracks is updated, only the mediaControlChannelDidUpdateStatus in connected senders will be notified.

Satisfy CORS Requirements

For adaptive media streaming, Google Cast requires the presence of CORS headers, but even simple mp4 media streams require CORS if they include Tracks. If you want to enable Tracks for any media, you must enable CORS for both your track streams and your media streams. So, if you do not have CORS headers available for your simple mp4 media on your server, and you then add a simple subtitle track, you will not be able to stream your media unless you update your server to include the appropriate CORS header. In addition, you need to allow at least the following headers: Content-Type, Accept-Encoding, and Range. Note that the last two headers are additional headers that you may not have needed previously.

Add a Custom Channel

The Cast framework retains the GCKCastChannel and GCKGenericChannel classes of Cast v2. The former class is meant to be subclassed to implement non-trivial channels that have associated state. The latter class is provided as an alternative to subclassing; it passes its received messages to a delegate so that they can be processed elsewhere.

In Cast v2, custom channels were enabled/disabled by registering/unregistering them with the GCKDeviceManager. That class is deprecated in CAF; channels must now be registered/unregistered with the GCKCastSession instead, using its -[addChannel:] and -[removeChannel:] methods.

The Cast framework provides two ways to create a channel to send custom messages to a receiver:

  1. GCKCastChannel is meant to be subclassed to implement non-trivial channels that have associated state.
  2. GCKGenericChannel is provided as an alternative to subclassing; it passes its received messages to a delegate so that they can be processed elsewhere.

Channels must be registered/unregistered with the GCKCastSession, using its -[addChannel:] and -[removeChannel:] methods.

Here is an example of a GCKCastChannel implementation:

// Header file
@interface HGCTextChannel : GCKCastChannel


// Implementation file
#import "HGCTextChannel.h"

@implementation HGCTextChannel
- (void)didReceiveTextMessage:(NSString*)message {
  NSLog(@"received message: %@", message);


A channel can be registered at any time; if the session is not currently in a connected state, the channel will automatically become connected when the session itself is connected, provided that the channel's namespace is present in the receiver app metadata's list of supported namespaces.

Each custom channel is defined by a unique namespace and must start with the prefix urn:x-cast:, for example, urn:x-cast:com.example.custom. It is possible to have multiple custom channels, each with a unique namespace. The receiver app can also send and receive messages using the same namespace.

HGCTextChannel *textChannel =
    [[HGCTextChannel alloc] initWithNamespace:@""];
[_castSession addChannel:textChannel];
[textChannel sendTextMessage:@"Hello World"];

To provide logic that needs to execute when a particular channel becomes connected or disconnected, override the -[didConnect] and -[didDisconnect] methods if using GCKCastChannel, or provide implementations for the -[castChannelDidConnect:] and -[castChannelDidDisconnect:] methods of the GCKGenericChannelDelegate if using GCKGenericChannel.

Use Queueing

The Cast framework provides queueing APIs that support the creation of lists of content items, such as video or audio streams, to play sequentially on the Cast receiver. The queue of content items may be edited, reordered, updated, and so forth.

Review the Google Cast Autoplay UX Guidelines for best practices when designing an autoplay/queueing experience for Cast.

The Cast receiver classes maintain the queue and responds to operations on the queue as long as the queue has at least one item currently active (playing or paused). Senders can join the session and add items to the queue. The receiver maintains a session for queue items until the last item completes playback or the sender stops the playback and terminates the session, or until a sender loads a new queue on the receiver. By default, the receiver does not maintain any information about terminated queues. Once the last item in the queue finishes, the media session ends and the queue vanishes.

Create and Load Media Queue Items

In iOS, a media queue item is represented in the Cast framework as a GCKMediaQueueItem instance. When you create a media queue item, if you are using the Media Player Library with adaptive content, you can set the preload time so that the player can begin buffering the media queue item before the item ahead of it in the queue finishes playing. Setting the item's autoplay attribute to true allows the receiver to play it automatically. For example, you can use a builder pattern to create your media queue item as follows:

GCKMediaQueueItemBuilder *builder = [[GCKMediaQueueItemBuilder alloc] init];
builder.mediaInformation = self.mediaInfo;
builder.autoplay = YES;
builder.preloadTime =
    [[NSUserDefaults standardUserDefaults] integerForKey:kPrefPreloadTime];
GCKMediaQueueItem *newItem = [builder build];

Load an array of media queue items in the queue by using the appropriate queueLoadItems method of the GCKRemoteMediaClient class.

Receive Media Queue Status Update

When the receiver loads a media queue item, it assigns a unique ID to the item which persists for the duration of the session (and the life of the queue). You can learn the status of the queue indicating which item is currently loaded (it might not be playing), loading, or preloaded. You can also get an ordered list of all the items in the queue. The GCKMediaStatus class provides this status information:

  • preloadedItemID property - The ID of the item that is currently preloaded, if any.
  • loadingItemID property - The ID of the item that is currently loading,
  • currentItemID property - The ID of the current queue item, if any.
  • queueItemCount method - Returns the number of items in the playback queue.
  • queueItemAtIndex method - Returns the item at the specified index in the playback queue.

Use these members together with the other media status members to inform your app about the status of the queue and the items in the queue. In addition to media status updates from the receiver, you can listen for changes to the queue by implementing GCKRemoteMediaClientListener.remoteMediaClientDidUpdateQueue.

Edit the Queue

To work with the items in the queue, use the queue methods of GCKRemoteMediaClient. you have several APIs. These let you load an array of items into a new queue, insert items into an existing queue, update the properties of items in the queue, make an item jump forward or backward in the queue, set the properties of the queue itself (for example, change the repeatMode that selects the next item), remove items from the queue, and reorder the items in the queue.

Supporting Autoplay

See Autoplay & Queueing APIs.

Override Image Selection and Caching

Various components of the framework (namely the Cast dialog, the mini controller, the expanded controller, and the GCKUIMediaController if so configured) will display artwork for the currently casting media. The URLs to the image artwork are typically included in the GCKMediaMetadata for the media, but the sender app may have an alternate source for the URLs. The GCKUIImagePicker protocol defines a means for selecting an appropriate image for a given usage and desired size. It has a single method, -[getImageWithHints:fromMetadata:], which takes a GCKUIImageHints object and a GCKMediaMetadata object as parameters, and returns a GCKImage object as a result. The framework provides a default implementation of GCKUIImagePicker which always selects the first image in the list of images in the GKCMediaMetadata object, but the app can provide an alternate implementation by setting the imagePicker property of the GCKCastContext singleton.

The GCKUIImageCache protocol defines a means of caching images that are downloaded by the framework via HTTPS. The framework provides oa default implementation of GCKUIImageCache which stores downloaded image files in the app's cache directory, but the app can provide an alternate implementation by setting the imageCache property of the GCKCastContext singleton.

Next Step

This concludes the features that you can add to your iOS Sender app. You can now build a sender app for another platform (Android or Chrome), or build a receiver app.