Andrey Chernukha
Andrey Chernukha

Reputation: 21808

How to run a non main queue in a background state?

Please take a look at this very simple piece of code:

dispatch_async(dispatch_get_main_queue(), ^{
   
    for (int i = 0; i < 100; i++)
    {
        NSLog(@"LOOP %d", i);
        sleep(1);
    }
});

If I send my app to the background state it is still running. Yet if I put the execution onto a non main queue like this:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    
    for (int i = 0; i < 100; i++)
    {
        NSLog(@"LOOP %d", i);
        sleep(1);
    }
});

then the execution is suspended when my app is going to the background. Is it possible to make it run in a background state when dispatched on a non main queue?

I need to mention that I'm running my app with these background modes enabled:

<key>UIBackgroundModes</key>
<array>
    <string>bluetooth-central</string>
    <string>voip</string>
</array>

Upvotes: 1

Views: 432

Answers (2)

Pranav Kasetti
Pranav Kasetti

Reputation: 9915

In addition to using performExpiringActivityWithReason, you can use the UIBackgroundTaskIdentifier API:

// Perform the task on a background queue.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
   // Request the task assertion and save the ID.
   self.backgroundTaskID = [[UIApplication sharedApplication]
              beginBackgroundTaskWithName: @"Finish Pending Tasks" expirationHandler:^{
       // End the task if time expires.
       [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskID];
       self.backgroundTaskID = UIBackgroundTaskInvalid;
   }];
        
   // Add your code here.
        
   // End the task assertion.
   [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskID];
   self.backgroundTaskID = UIBackgroundTaskInvalid;
};

The expiration handler is what gets called before the app is killed, but don’t plan on doing computationally expensive tasks using this method since the OS has system-wide time limits which are out of the developer’s control.

If you need to perform specific tasks in the background for certain system events like network fetches, then consider using a Background Mode.

bg modes ios

The advantage of using the UIKit API, in this case, is you can query [[UIApplication sharedApplication] backgroundTimeRemaining] within your for loop to perform any last-minute cleanup steps.

Note that if you’re using App Extensions Apple recommend using the NSProcess API, so advice will vary depending on the use case.

Upvotes: 2

CSmith
CSmith

Reputation: 13458

Please try using NSProcessInfo performExpiringActivityWithReason API

[[NSProcessInfo processInfo] performExpiringActivityWithReason:@"myReason" usingBlock:^(BOOL expired)
{
  // This block is run on a separate (background) thread
  // Put your code here...
}

please note this is only a request for some additional CPU time on a process that is being backgrounded ... you cannot run background code indefinitely.

The block will be invoked a 2nd time with expired == YES when you're about to be killed.

Upvotes: 1

Related Questions