How to troubleshoot iOS background app fetch not working? How to troubleshoot iOS background app fetch not working? ios ios

How to troubleshoot iOS background app fetch not working?


Your problem is that you are returning from performFetchWithCompletionHandler before you call the completion handler, since the network fetch operation is occurring the in the background and you call the completion handler in your delegate method. Since iOS thinks you aren't playing by the rules it will deny your ability to use background fetch.

To fix the problem you need to call beginBackgroundTaskWithExpirationHandler and then end that task after you have called the completion handler.

Something like:

UIBackgroundTaskIdentifier backgroundTask-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{    fetchCompletionHandler = completionHandler;    fetchStart = [NSDate date];    self.backgroundTask = [application beginBackgroundTaskWithExpirationHandler:^{        [application endBackgroundTask:self.backgroundUpdateTask];        self.backgroundTask = UIBackgroundTaskInvalid;    }];    [[NSUserDefaults standardUserDefaults] setObject:fetchStart forKey:kLastCheckedContentDate];    [FeedParser parseFeedAtUrl:url withDelegate:self];}-(void)onParserFinished{    DDLogVerbose(@"AppDelegate/onParserFinished");    UIBackgroundFetchResult result = UIBackgroundFetchResultNoData;    NSDate *fetchEnd = [NSDate date];    NSTimeInterval timeElapsed = [fetchEnd timeIntervalSinceDate:fetchStart];    DDLogVerbose(@"Background Fetch Duration: %f seconds", timeElapsed);    if ([self.mostRecentContentDate compare:item.date] < 0) {        DDLogVerbose(@"got new content: %@", item.date);        self.mostRecentContentDate = item.date;        [self scheduleNotificationWithItem:item];        result = UIBackgroundFetchResultNewData;    }    else {        DDLogVerbose(@"no new content.");        UILocalNotification* localNotification = [[UILocalNotification alloc] init];        localNotification.alertBody = [NSString stringWithFormat:@"Checked for new posts in %f seconds", timeElapsed];        [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];    }    fetchCompletionHandler(result);    [[UIApplication sharedApplication] application endBackgroundTask:self.backgroundUpdateTask];    self.backgroundTask = UIBackgroundTaskInvalid;}

My test app using this approach has been executing a fetch every 15 minutes initially, but it becomes less frequent over time. Without the background task it exhibited the same issue you are seeing.

I found that setting the background fetch interval to something other than UIApplicationBackgroundFetchIntervalMinimum also helps. My test app is running with a background fetch interval of 3600 (one hour) and has been reliably triggering for several days now; even after a phone restart and not running the app again. The actual trigger interval is 2-3 hours however.

My sample app is here


In order to implement background fetch there are three things you must do:

  • Check the box Background fetch in the Background Modes of your app’s Capabilities.
  • Use setMinimumBackgroundFetchInterval(_:) to set a time interval appropriate for your app.
  • Implement application(_:performFetchWithCompletionHandler:) in your app delegate to handle the background fetch.

Background Fetch Frequency

How frequent our application can perform Background Fetch is determined by the system and it depends on:

  • Whether network connectivity is available at that particular time
  • Whether the device is awake i.e. running
  • How much time and data your application has consumed in the previous Background Fetch

In other words, your application is entirely at the mercy of the system to schedule background fetch for you. In addition, each time your application uses background fetch, it has at most 30 seconds to complete the process.

Include in your AppDelegate (change the below code to your fetching needs)

-(void) application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {    NSLog(@"Background fetch started...");    //---do background fetch here---    // You have up to 30 seconds to perform the fetch    BOOL downloadSuccessful = YES;    if (downloadSuccessful) {        //---set the flag that data is successfully downloaded---        completionHandler(UIBackgroundFetchResultNewData);    } else {        //---set the flag that download is not successful---        completionHandler(UIBackgroundFetchResultFailed);    }    NSLog(@"Background fetch completed...");}- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{    [[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];    return YES;}

To check if Background Refresh is enabled for your app use the below code:

UIBackgroundRefreshStatus status = [[UIApplication sharedApplication] backgroundRefreshStatus];switch (status) {    case UIBackgroundRefreshStatusAvailable:    // We can do background fetch! Let's do this!    break;    case UIBackgroundRefreshStatusDenied:    // The user has background fetch turned off. Too bad.    break;    case UIBackgroundRefreshStatusRestricted:    // Parental Controls, Enterprise Restrictions, Old Phones, Oh my!    break;}