Using NSURLSession with BGAppRefreshTask fails with error "Lost connection to background transfer service"

So I'm trying to migrate some code out of the deprecated method:

Code Block
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler API_DEPRECATED("Use a BGAppRefreshTask in the BackgroundTasks framework instead", ios(7.0, 13.0), tvos(11.0, 13.0));


And into a BGAppRefreshTask. Basically what's done here is the app downloads some content and displays notification in the the Notification Center.

When I run the app on the device fire off the task via the debugger like so: 

Code Block
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.refreshidhere"];


The task fires. But any NSURLSessionTask instances I create fail and I get an error:

finished with error [-997] Error Domain=NSURLErrorDomain Code=-997 "Lost connection to background transfer service"


So I just made a small sample project here and still getting the error. Here is how it's configured:

Code Block
-(void)handleAppRefreshWithTask:(BGAppRefreshTask*)task
{
    // Fetch the latest feed entries from server.
    [self scheduleAppRefresh];
    if (self.runningAppRefreshTask != nil)
    {
        NSLog(@"already running app refresh task...");
        [task setTaskCompletedWithSuccess:YES];
        return;
    }
    self.runningAppRefreshTask = task;
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.apple.com"]];
    NSURLSessionDataTask *dataTask = [self.backgroundURLSessionForTest dataTaskWithRequest:request];
    
    [task setExpirationHandler:^{
        NSLog(@"Expiration handler called...");
        [dataTask cancel];
    }];
    [dataTask resume];
}
#pragma mark - NSURLSessionDataDelegate
-(void)URLSession:(NSURLSession*)session
         dataTask:(nonnull NSURLSessionDataTask*)dataTask
   didReceiveData:(nonnull NSData*)data
{
    [self.backgroundDownloadData appendData:data];
}
-(void)URLSession:(NSURLSession*)session
             task:(NSURLSessionDataTask*)task
didCompleteWithError:(nullable NSError*)error
{
  NSData *theData = [self.backgroundDownloadData copy];
 dispatch_async(dispatch_get_main_queue(), ^{
    if (error == nil)
    {
        NSLog(@"got %@",theData);
       [self.runningAppRefreshTask setTaskCompletedWithSuccess:YES];
    }
    else
    {
        NSLog(@"error: %@",error);
       [self.runningAppRefreshTask setTaskCompletedWithSuccess:NO];
    }
    self.runningAppRefreshTask = nil;
    [self.backgroundDownloadData setData:[[NSData alloc]init]];
});
}

For some reason if I change the task from BGAppRefreshTask to a BGProcessingTask, and set the BGProcessingTaskRequest’s requiresNetworkConnectivity = YES…the background url session suddenly works when I test the simulate the task from the debugger.

I’ve been unable to get BGAppRefreshTask to work with NSURLSession…even though the documentation states that BGAppRefreshTask is the replacement API for -application: performFetchWithCompletionHandler:

I’m testing on iOS 13.
It seems if I remove the call to dispatch_async on the main queue in the NSURLSessionDataTaskDelegate method, the BGAppRefreshTask seems to be more reliable. However when dispatching on the main queue the app seems to just simply remain suspended until the app re-enters the foreground.

This is a problem because my app downloads data and must post notifications on the main thread in order for areas in the UI to refresh to reflect the new content. Also my app uses a database, where queries are execute on a particular database queue. And the networking code that downloads data uses its own queue.

So networking queue downloads data -> dispatches to database queue inserts new content -> dispatches to main queue and notifies UI elements of content refresh.

Simply just suspending the app immediately after dispatching to another queue from within a background task is pretty strict and makes composing reusable operations difficult. BGAppRefreshTask in this current form does not seem very usable except for the simplest cases.
Using NSURLSession with BGAppRefreshTask fails with error "Lost connection to background transfer service"
 
 
Q