Mahdi
Mahdi

Reputation: 29

Swift - How to make sure app is continue fetching data in background?

I have an iOS application where the user can trigger a lengthy update process by tapping on a button. The update process involves making API calls that can take a few minutes to complete. Everything works fine when the user stays in the foreground, but if the user switches to the background, the app is not able to fetch all the necessary data.

I would like to implement a solution where, upon tapping the update button, the update process can continue running even if the app enters the background. I want to request additional background execution time to ensure that the update process completes successfully.

Based on my research, I've learned about the beginBackgroundTask(expirationHandler:) method provided by the UIApplication class, which allows an app to request additional background execution time. However, I'm unsure how to implement it correctly in my app.

Here's the current flow in my app:

The user taps the "Update" button, triggering the updateData() function. Inside updateData(), I make the necessary API calls to fetch the required data. If the app is in the foreground, everything works fine. However, if the app enters the background during the update process, the data fetching is incomplete. I would like to modify the updateData() function to request additional background execution time so that the update process can continue running until it completes, regardless of whether the app is in the foreground or background.

Could you provide guidance on how to implement this correctly? Any code examples or suggestions would be greatly appreciated.

PS. If I use a BGTask as I understand the BGTask only runs when user goes to the background mode. Is there any way to run the updateData() with a BGTask wether the app stays in foreground or background? The user might stay in foreground until the update finishes or they may go to the background. That's my main issue.

Thank you!

Upvotes: 0

Views: 1324

Answers (1)

dgatwood
dgatwood

Reputation: 10417

First, beginBackgroundTask and BGTask are somewhat orthogonal. The beginBackgroundTask or beginBackgroundTaskWithName method might be appropriate for your use case. The latter, as I understand it, is mostly for doing updates while your app isn't running, so that's probably not what you're trying to do.

For the beginBackgroundTask approach, I would create a single background task independent of your network requests right before you issue the first outstanding network request, and then mark the background as done when the last task has completed and the data has been processed.

For example, if you put all your network tasks in a single NSURLSession, your completion handler block could end with a call like this:

[someAppSpecificObject callBackgroundTaskCompletionIfNeededForSession:session];

and that method would do something like this:

@property(nonatomic) NSMutableDictionary *<NSURLSession *, NSNumber *> *backgroundTaskIdentifiers;

- (void)callBackgroundTaskCompletionIfNeededForSession:(NSURLSession *)session {
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    [session getAllTasksWithCompletionHandler:^(NSArray<NSURLSessionTask *> *tasks) {
      if (!tasks.count) {
        UIBackgroundTaskIdentifier identifier = [self. backgroundTaskIdentifiers[session] integerValue];
        [[UIApplication sharedApplication] endBackgroundTask:identifier];
      }
    }
  }
}

or if you have the potential for multiple requests outstanding that are not all queued up on the session at once, do something similar using the internal data structure of your choice.

However, if it is at all possible, it would be better to use an NSURLSessionDownloadTask to kick off the long-running job, and rely on the app getting woken up or launched in the background to process the data when the task completes. That approach is friendlier on the battery, with the caveat that issuing additional requests when the long-running request completes may result in rapidly diminishing performance, so if that is needed, it might be better to save the data, and complete the processing work when the app is next in the foreground.

Upvotes: 0

Related Questions