2

While my app is in the background, I want to upload many files using NSURLSessionUploadTask.

With a suitably configured NSURLSession object, the API for queueing background uploading is:

NSURLSessionUploadTask *dataTask = [sessionObj uploadTaskWithRequest:urlRequest
                                                            fromFile:localFilePath];
[dataTask resume];

When the upload completes -- success or failure -- I get this callback in the background:

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error

During the above callback, I queue another upload with
-uploadTaskWithRequest:fromFile:.

But after some files are uploaded, the callbacks stop, and so does the uploading.

Is there something I'm missing to keep uploads going? E.g. do I need to put some extra code on this callback to keep the uploads going?

-(void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
    AppDelegate *appDelegate = (id)[[UIApplication sharedApplication] delegate];
    if (appDelegate.backgroundSessionCompletionHandler) {
        void (^completionHandler)() = appDelegate.backgroundSessionCompletionHandler;
        appDelegate.backgroundSessionCompletionHandler = nil;
        completionHandler();
    } 
}

Note: I have already read this related SO question, but it didn't help.

Community
  • 1
  • 1
Ekra
  • 3,241
  • 10
  • 41
  • 61
  • It sounds like upload of one file is simply hanging; maybe the server cannot handle it. Try implementing URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend: to see whether there is progress. In general, implement all methods in the delegate protocol to see which one does get called. – fishinear Nov 14 '13 at 13:13
  • didsendBodydata gets only called when the app is in foreground for background upload it never gets called. We only get task didCompleteWithError: callback when the upload is finished – Ekra Nov 14 '13 at 13:52
  • I did not understand that you are doing this from an app that is running in the background; you should make that clear in your question. Read the Apple docs on background app's, there are a lot of restrictions that apply. – fishinear Nov 14 '13 at 14:03

1 Answers1

2

Keep the task queue topped off

I've found that, in the background, you should avoid letting the task queue reach 0 tasks until you are really "done". Instead, you get better continuity if you always keep few tasks in the queue at all times. For instance, when the number of tasks gets down to 3, add 3 more.

Protip: for a count of active tasks, you're better off with your own tally, rather than clumsy async -[NSURLSession getTasksWithCompletionHandler:]. I add/remove the background task ID (as @(taskID)) to an NSSet (e.g. NSSet *activeTaskIDs), and use the count from that. I spell this out here.

Bracket async calls

If you do anything async during the the didComplete... callback, you must surround that with UIApplication's -beginBackgroundTaskWithExpirationHandler: and -endBackgroundTask:. Otherwise, it looks like you've popped the stack, and iOS will kill you.

Community
  • 1
  • 1
Clay Bridges
  • 11,602
  • 10
  • 68
  • 118
  • Unfortunately, you often don't know when the number of tasks gets down to 3. If your app is in the background and your device goes to sleep, it will continue uploading until all tasks are completed, but not notify your app until some time later. I have seen hundreds of calls to `- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error` come in at once. – landweber Apr 03 '14 at 04:48
  • My experience has been that the `didComplete...` call seems to be relatively close to actual task completion. Maybe b/c I keep my queue small (~25 tasks max), or I'm doing uploads, or I'm just not "in the field" :). Variables abound! Regardless, if you enqueue new tasks during `didComplete`, that probably comes close enough to "not letting it get down to zero". – Clay Bridges Apr 03 '14 at 13:52