title: How do I implement background fetch using a data task?
body: <p>I'm trying to implement background fetch in an iOS app. In the official <a href="https://developer.apple.com/documentation/foundation/url_loading_system/downloading_files_in_the_background?language=objc" rel="nofollow noreferrer">documentation</a> is stated:</p>
  
  <p><em>"You don’t have to do all background network activity with background sessions ... ... . Apps that declare appropriate background modes can use default URL sessions and data tasks, just as if they were in the foreground."</em></p>
  
  <p>Moreover, in this WWDC 2014 <a href="https://developer.apple.com/videos/play/wwdc2014/707/" rel="nofollow noreferrer">video</a> at 51:50 between the best practices is stated:</p>
  
  <p><em>"One common mistake that we think some people have made in the past is assuming that when running in the background to handle things like a background fetch update... ...they have been required to use background session. Now, using background upload or download... ...works really well, but in particular for large downloads. When you're running for a background fetch update ... you have about 30 to 60 seconds to run in the background" [before the application gets suspended] "If you have same small networking task that could finish within this time it is perfectly ok to do this in an in-process or default NSURLSession ... ..."</em></p>
  
  <p>I am exactly in the situation of a small networking task: I want to communicate with a rest api on our server, to post same data and get back the data response. I'm creating a single default session and executing one or two tasks, synchronising them using a dispatch group, like in this example:</p>
  
  <pre><code>- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
      NSLog(@"AppDelegate application:performFetchWithCompletionHandler:");
      BOOL thereIsALoggedInUser = [self thereIsALoggedInUser];
      if (!(thereIsALoggedInUser &amp;&amp; [application isProtectedDataAvailable])){
          completionHandler(UIBackgroundFetchResultNoData);
          NSLog(@"completionHandler(UIBackgroundFetchResultNoData): thereIsALoggedInUser: %@, isProtectedDataAvailable: %@",
            thereIsALoggedInUser ? @"YES" : @"NO",
            [application isProtectedDataAvailable] ? @"YES" : @"NO");
          return;
      }
  
      NSArray&lt;NSString*&gt; *objectsIWantToSend = [self getObjectsIWantToSend];    
      if (!objectsIWantToSend) {
          [[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalNever];
          completionHandler(UIBackgroundFetchResultNoData);
          NSLog(@"No post data");
      } else {
          [self sendToServer:objectsIWantToSend usingCompletionHandler:completionHandler];
      }
  }  
  
  -(void) sendToServer:(NSArray&lt;NSString*&gt; *)objectsArray usingCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
      NSLog(@"AppDelegate - sendToServer:usingCompletionHandler:");
      __block BOOL thereWasAnError = NO;
      __block BOOL thereWasAnUnsuccessfullHttpStatusCode = NO;    
  
      dispatch_group_t sendTransactionsGroup = dispatch_group_create();
  
      NSURLSession *postSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
  
      for (NSString *objectToPost in objectsArray) {
  
          dispatch_group_enter(sendTransactionsGroup);
  
          NSMutableURLRequest *request = [NSMutableURLRequest new];
          [request setURL:[NSURL URLWithString:@"restApiUrl"]];
          [request setHTTPMethod:@"POST"];
          request = [Util setStandardContenttypeAuthorizationAndAcceptlanguageOnRequest:request];
          [request setHTTPBody:[NSJSONSerialization dataWithJSONObject:@{ @"appData": objectToPost } options:kNilOptions error:nil]];
  
          NSURLSessionDataTask *dataTask = [postSession dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
  
              NSInteger statusCode = -1;
              if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
                  statusCode = [(NSHTTPURLResponse *)response statusCode];
              }
  
              if (error) {
                  NSLog(@"Error");
                  thereWasAnError = YES;
              } else if (statusCode != 201) {
                  NSLog(@"Error: http status code: %d", httpResponse.statusCode);
                  thereWasAnUnsuccessfullHttpStatusCode = YES;
              } else {
                  [self parseAndStoreData:data];
              }
  
              dispatch_group_leave(sendTransactionsGroup);
          }];
  
          [dataTask resume];
      }
  
      dispatch_group_notify(sendTransactionsGroup, dispatch_get_main_queue(),^{
          if (thereWasAnError) {
              completionHandler(UIBackgroundFetchResultFailed);
          } else if (thereWasAnUnsuccessfullHttpStatusCode) {
              [[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalNever];
              completionHandler(UIBackgroundFetchResultNoData);
          } else {
              completionHandler(UIBackgroundFetchResultNewData);
          }
      });
  }
  </code></pre>
  
  <p>In my application:didFinishLaunchingWithOptions: method I am setting the BackgroundFetchInterval to the MinimumInterval using</p>
  
  <pre><code>[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
  </code></pre>
  
  <p>I am not force-quitting the app on the device because I know that the system in that case won't call the AppDelegate application:performFetchWithCompletionHandler: method until a user restarts the app</p>
  
  <p>I'm using several test cases with several combinations of devices locked/unlocked, charging/not charging, all under a wifi network because they've got no sims</p>
  
  <p>When I run the aforementioned kind of code using Xcode -> Debug -> Simulate background fetch or using a build scheme with the "launch due to a background fetch event" option selected all goes well: the server gets my post request, the app correctly has the server response back, and the dispatch_group_notify block gets executed. Testing the code through an actual device I get instead no result, not even the call to the server</p>