1

I notice in my app that when application enter background while loading causes the error such as "timeout" or "host name not found" .

It is due to the process that does not allow connection to run in background for a long time.

But that kind of error message make it bad for user experience. So what should I do to cancel the transaction ? Should I just cancel all the connection ? I tried to search the Q&A in SO here but can't find an answer.

For more information, my app use NSURLConnectionDelegate Method. I have a store singleton that manage all connection to my server. NSURLConnection is called and managed in custom object also.

I tried to just [connection cancel] in - applicationDidEnterBackground: but that make the UI broken because I load data to put into UITableViewCell ,etc. Can anyone point to the example to solve this kind of problem?

Updated Code:

- (void)applicationDidEnterBackground:(UIApplication *)application
{
 __block UIBackgroundTaskIdentifier backgroundTask; backgroundTask = 
[application      beginBackgroundTaskWithExpirationHandler: ^ { 
[application endBackgroundTask:backgroundTask]; 
backgroundTask = UIBackgroundTaskInvalid; }]; } 

  }

Can I just put this code in the appDelegate ? What is the drawback for just doing this versus put the beginBackgroundTaskWithExpirationHandler before the task that I want to keep running in background and endBackgroundTask after that task finished ? My code has one object that deal directly to NSURLConnection.

Kong Hantrakool
  • 1,865
  • 3
  • 20
  • 35

2 Answers2

3

You are allowed to keep running an NSURLConnection for some period of time after you go into the background. Apple doesn't publish the exact period of time, but it's 10 minutes. See Executing a Finite-Length Task in the Background for details on how to use beginBackgroundTaskWithExpirationHandler: to request more time to complete your download.

In most cases you shouldn't proactively cancel your download. You should wait until the system expires you and then deal with the error at that point. If your download is brief, there's no reason to cancel it (in most cases, the most expensive thing about a connection is setting it up in the first place). If it's a very long download, then the user is going to be annoyed if it doesn't proceed in the background. (This assumes that you're downloading things because the user requested it.)

webo80
  • 3,365
  • 5
  • 35
  • 52
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • Thanks for your suggestion. My app will have another object dealing with downloading stuff .So in the applicationDidEnterBackground delegate. Can I just call __block UIBackgroundTaskIdentifier backgroundTask; backgroundTask = [application beginBackgroundTaskWithExpirationHandler: ^ { [application endBackgroundTask: backgroundTask]; backgroundTask = UIBackgroundTaskInvalid; }]; } ? The handlerBlock will be called after 10min when app entered background .So it will endBackgroundTaks anyway. This one looks easier but I am not if it waste much resource – Kong Hantrakool Aug 04 '13 at 11:32
1

First, it's better to have this call on the application delegate, so that the View in the NavigationController can be closed. Second, mark beginning of the background processing with beginBackgroundTaskWithExpirationHandler: and end it with endBackgroundTask: like this:

.h:

UIBackgroundTaskIdentifier bgTask;

.m:

- (void)sendPhoto:(UIImage *)image
{
  UIApplication *app = [UIApplication sharedApplication];

  bgTask = [app beginBackgroundTaskWithExpirationHandler:^{ 
    [app endBackgroundTask:bgTask]; 
    bgTask = UIBackgroundTaskInvalid;
  }];


  NSLog(@"Sending picture...");

  // Init async NSURLConnection

  // ....
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {

  NSLog(@"Picture sent.");

  UIApplication *app = [UIApplication sharedApplication];

  if (bgTask != UIBackgroundTaskInvalid) {
    [app endBackgroundTask:bgTask]; 
    bgTask = UIBackgroundTaskInvalid;
  }
}

Also remember one thing its important:

You have 10 minutes before iOS terminates your app. You can check this time with [app backgroundTimeRemaining]

Rajeev Barnwal
  • 1,349
  • 11
  • 14
  • Thanks for your answer. But I am not quite clear. Do you mean that I should put that kind of code in controller or in appDelegate ? – Kong Hantrakool Aug 03 '13 at 18:44
  • Or I can just put this code in appDelegate - (void)applicationDidEnterBackground:(UIApplication *)application { __block UIBackgroundTaskIdentifier backgroundTask; backgroundTask = [application beginBackgroundTaskWithExpirationHandler: ^ { [application endBackgroundTask: backgroundTask]; backgroundTask = UIBackgroundTaskInvalid; }]; } – Kong Hantrakool Aug 03 '13 at 18:44
  • Yes you can just put this code inside your app Delegate and it will be good to go! – Rajeev Barnwal Aug 03 '13 at 19:02
  • So what is a differences between your code and the one that I posted in the comment? – Kong Hantrakool Aug 04 '13 at 06:55