0

I have a background task that stop a streamer after 30 minutes as follows:

- (void)applicationDidEnterBackground:(UIApplication *)application
{


bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
    [[UIApplication sharedApplication] endBackgroundTask:bgTask];
    bgTask = UIBackgroundTaskInvalid;
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    while ([[NSDate date] timeIntervalSinceDate:[[NSUserDefaults standardUserDefaults]objectForKey:@"date"]]<30) {
        NSLog(@"<30");

         [NSThread sleepForTimeInterval:1];


     }
    NSLog(@"Stop");
    [main stopStreaming];


 });
}

but the problem is when the user Did Enter Background the bgTask called again, thats mean if the user entered the background 10 times he will have 10 back ground UIBackgroundTaskIdentifier

That cause the streamer playing badly and the NSLog(@"<30"); get called more than one time at the same second.

Please Advice.

Mutawe
  • 6,464
  • 3
  • 47
  • 90

1 Answers1

1

You'll have to keep track of the background tasks you have started and make sure you don't do the work of previous tasks when a new task is started. You can do this easily by keeping an NSInteger around in your app delegate and incrementing it for each time.

But the easier way is just this: (in place of your dispatch_async call)

SEL methodSelector = @selector(doThisAfter30Seconds);
[[self class] cancelPreviousPerformRequestsWithTarget:self selector:methodSelector object:nil];
[self performSelector:methodSelector  withObject:nil afterDelay:30];

This will set a timer for 30 seconds and make sure the previous timers are not run. Then just implement - (void)doThisAfter30Seconds to do whatever you want.

(You will probably want to check in doThisAfter30Seconds that the task is still in the background, or manually remove the timer with cancelPreviousPerformRequestsWithTarget:... in applicationWillEnterForeground:.)

Jesper
  • 7,477
  • 4
  • 40
  • 57
  • Great answer, i didn't think a moment that performSelector:withObject:afterDelay: will work in the background.. BTW is this way guaranteed to work if i set the time for 3 hours ?? – Mutawe Jun 17 '13 at 09:17
  • It's not guaranteed to work for three hours. It's guaranteed to work for as long as you have permission to run in the background, which is up to the OS, but is usually far longer than 30 seconds. Note that you should probably also `cancelPreviousPerformRequestsWithTarget:...` in the expiration handler to avoid letting `doThisAfter30Seconds` run when the app comes to the foreground after having been suspended. – Jesper Jun 17 '13 at 09:21
  • And the reason it works in the background is because the non-UI parts of your application still runs as usual in the background. If timers stopped working, a surprising amount of stuff would stop working along with it. – Jesper Jun 17 '13 at 09:31
  • As i understood from you the iOS take the permission from me for controlling the app after a certain time?? – Mutawe Jun 17 '13 at 09:34
  • When you ask iOS to `beginBackgroundTaskWithExpirationHandler:`, you will get a number representing the background task. When iOS thinks you've had enough time, it will call the handler and when that's finished it will suspend your app. This time is visible as `backgroundTimeRemaining` on `UIApplication`. When the user switches to the app again, it is either resumed from its last point or (if it has been killed to regain resources) started from scratch. If a timer is set to trigger after the point of suspension, it will be triggered when the app resumes. Apple's documentation has more on this. – Jesper Jun 17 '13 at 10:34