0

Using the iOS Sender API Framework, when my application goes in the background, the SDK tears down all connections and I cannot launch any more media until the app is put back in the foreground. My app plays audio and is allowed to run and stream in the background. Is there an option to tell Googlecast framework to keep the sockets opened?

Here's the log on backgrounding:

INFO: Will resign active

INFO: Now in background

FINE: -[GCKCastSocket disconnect] disconnect

FINE: -[GCKCastSocket doTeardownWithError:] doTeardownWithError

FINE: -[GCKCastSocket doTeardownWithError:] notifying delegate that socket is disconnected

FINE: -[GCKHeartbeatChannel didDisconnect] disconnected - stopping heartbeat timer if necessary

FINE: -[GCKCastSocket socketDidDisconnect:withError:] socketDidDisconnect:withError: "(null)"

Then when the app resumes: INFO: Going into foreground

FINE: -[GCKCastSocket doTeardownWithError:] doTeardownWithError

FINE: -[GCKCastSocket doTeardownWithError:] notifying delegate that socket is disconnected

FINE: -[GCKCastSocket connectToHost:port:withTimeout:] Connecting to "192.168.1.4" on port 8009l...

INFO: Did become active

FINE: -[GCKCastSocket socket:didConnectToHost:port:] socketDidConnect:

FINE: -[GCKCastSocket socketDidSecure:] socketDidSecure:

FINE: -[GCKCastSocket socket:didReadData:withTag:] prefix read, expected message length=1307

FINE: -[GCKDeviceAuthChannel didReceiveBinaryMessage:] Genuine Google device didDeviceAuthenticated=YES

FINE: -[GCKDeviceManager deviceAuthChannelDidAuthenticate:] Authentic device, connecting receiver channel

FINE: -[GCKCastSocket socket:didReadData:withTag:] prefix read, expected message length=474

FINE: -[GCKDeviceManager receiverControlChannel:didReceiveStatusForApplication:] Application to join (CC1AD845) is available

FINE: -[GCKDeviceManager connectAndNotifyDidConnectToApplication:launchedApplication:] Connected to application <0x1467c8f0:GCKApplicationMetadata> Default Media Receiver (CC1AD845), transport ID web-11

FINE: -[GCKCastSocket socket:didReadData:withTag:] prefix read, expected message length=135

FINE: -[GCKMediaControlChannel didReceiveTextMessage:] message received: {"type":"MEDIA_STATUS","status":[],"requestId":6}

c0diq
  • 144
  • 1
  • 3
  • Can you show the code for your appDelegate? In particular your ApplicationDidEnterBackground method - you will need to schedule a background task in this method. Additionally, make sure you are holding a reference to the sender object from your AppDelegate, not your viewcontroller – Paulw11 Mar 20 '14 at 19:51

2 Answers2

2

With the version 2.0 of the iOS Sender API, the GCKCastSocket is closed upon receiving a UIApplicationDidEnterBackgroundNotification and this is not something that can be configured.

That means:

  • no new media can be pushed from the app to the chromecast while the app is in the background
  • it is not possible to implement lock screen controls

Alternatives (custom receiver only):

  • Send a list of media to be played to the receiver
  • Fetch new media from the cloud directly from the receiver

See also this question or this one for more details.

  • 1
    There's actually a workaround that I used in my AirMusic app to stay connected while in the background. Check it out: https://itunes.apple.com/us/app/airmusic/id409782234?mt=8 – c0diq Apr 22 '14 at 19:50
  • @c0diq and which is that workaround? What I did some years ago - before iOS7 new background mode was this http://stackoverflow.com/questions/15448868/app-for-jailbroken-ios-device-consistent-background-operation/16068213#16068213 Which is your solution? – loretoparisi Jun 26 '14 at 22:09
  • @c0diq share the love. – Jon Willis Aug 27 '14 at 17:15
0

This is a solution I used for other needs, but I guess it could be applied here as well (not tested yet)

1. Create a background task handlers as dispatch_block_t, let's say

 dispatch_block_t myDummyBackgroundTaskBlock = {
    [[UIApplication sharedApplication] endBackgroundTask:myDummyBackgroundTask];
    myDummyBackgroundTask = UIBackgroundTaskInvalid;
    myDummyBackgroundTask = [app beginBackgroundTaskWithExpirationHandler:myDummyBackgroundTask];
};

2. Define somewhere this background and foreground tasks handler

    // foreground
    -(void)handleTasksForApplicationInForeground {
    if(myDummyBackgroundTask) { // reset that task
       [[UIApplication sharedApplication] endBackgroundTask: myDummyBackgroundTask];
       myDummyBackgroundTask = UIBackgroundTaskInvalid;
     }
    }

     // background
     -(void) handleTasksForApplicationInBackground {
         UIDevice *device = [UIDevice currentDevice];
         BOOL backgroundSupported = NO;
        if ([device respondsToSelector:@selector(isMultitaskingSupported)])
         backgroundSupported = device.multitaskingSupported;
        if(backgroundSupported && backgroundEnabled) { // perform a background task

            myDummyBackgroundTaskBlock = ^{
                [[UIApplication sharedApplication] endBackgroundTask: myDummyBackgroundTaskBlock];
                myDummyBackgroundTaskBlock = UIBackgroundTaskInvalid;
           };

           SEL sel = @selector(doDummyBackgroundTask);
           [self doBackgroundTaskAsync:sel];

           [self performSelector:@selector(doBackgroundTaskAsync:) withObject:nil afterDelay:500.0f]; /// LP: this is the funny part since iOS will kill the task after 500 sec.
             }
           }

3. Now let's handle in the application delegate the background mode (As defined before you can activate background mode with different options in your app .plist):

    -(void)applicationDidEnterBackground:(UIApplication *)application {
       [self handleTasksForApplicationInBackground];
     }

    -(void)applicationWillEnterForeground:(UIApplication *)application {
       [self handleTasksForApplicationInForeground];
     }

4. Let's see what the background async task selector does

        -(void) doBackgroundTaskAsync:(SEL)selector {

        @try {
             if( [[UIApplication sharedApplication] backgroundTimeRemaining] < 5 ) { 
               return;
             }

           if(!myDummyBackgroundTaskBlock) { // need to create again on-the-fly
                myDummyBackgroundTaskBlock = ^{
                      [[UIApplication sharedApplication] endBackgroundTask:myDummyBackgroundTask];
                     myDummyBackgroundTask = UIBackgroundTaskInvalid;
                };
             }

            myDummyBackgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:myDummyBackgroundTaskBlock];
          dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{

           while ([[UIApplication sharedApplication] backgroundTimeRemaining] > 5.0) {
              int delta = 5.0;
              [self performSelector: selector ];
              sleep(delta);
          }
        });
      }
       @catch (...) {
     }
    }
loretoparisi
  • 15,724
  • 11
  • 102
  • 146