Srikar Appalaraju
Srikar Appalaraju

Reputation: 73588

iOS Background downloads when the app is not active

When my app is initially downloaded, the user needs to download a large file something like 200MB (upper limit). I definitely can't expect the user to keep the app open till this file is downloaded. So he/she might close the app & the app will go into background.

How can I continue to download the file in this scenario? Is this even possible in iOS?

Upvotes: 23

Views: 39030

Answers (8)

cat
cat

Reputation: 2895

Unfortunately most answers here are outdated in 2020! iOS 13 reduces the time of -beginBackgroundTaskWithExpirationHandler: to seconds from minutes and also kills the app with adverse effects. I also consider disabling auto screensaver mode for the duration of the download. More information can be found here:

Upvotes: 0

Chris Gummer
Chris Gummer

Reputation: 4782

It is possible to start a background task when you begin the download:

Apps that are transitioning to the background can request an extra amount of time to finish any important last-minute tasks.

Executing a Finite-Length Task in the Background

However, such a task is limited to an undefined amount of execution time by the system. However, a 200Mb file download may be too large a task in this case.

Upvotes: 7

Marimuthu
Marimuthu

Reputation: 447

You could use NSURLSession introduced in iOS 7.0 and later to continue the uploading/download of an file even when the app is suspended.

Apple documentation

The NSURLSession class and related classes provide an API for downloading content. This API provides a rich set of delegate methods for supporting authentication and gives your app the ability to perform background downloads when your app is not running or, in iOS, while your app is suspended.

And

Background sessions let you perform uploads and downloads of content in the background while your app is not running. You can create a background session configuration by calling the backgroundSessionConfiguration: method on the NSURLSessionConfiguration class.

A cool tutorial to use NSURLSession at this link.

Upvotes: 7

Add080bbA
Add080bbA

Reputation: 1876

Build a newsStand iOS application; not a standart iOS application.

Standart iOS aplications:

  • can not download data when closed.
  • can download data in limited time when pushed to background.
  • Not enough time to download 200mb.

NewsStand applications have an extra time privilege to download when closed or inBackground. Once in every 24 hours, they have a privilege to answer a remoteContentReady delegate call.

Building a newsStand application is no different than building a standart iOS application. You will just mark it as newStand application.

Then you will have privilege to run code which is inside remoteContentReady delegation.

To receive remoteContentReady signal, you have to send a signal from your serverside (c#, php, bla bla).

Not to every single iOS app. Something like a remoteNotification signal. You may need ssl certification for your website in order to accomplish this.

Best Regards

Upvotes: 0

srjohnhuang
srjohnhuang

Reputation: 332

#pragma mark - View Loading handling
- (void)viewDidLoad
{
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
}

#pragma mark - Background handling when application goes background
UIBackgroundTaskIdentifier background_task;
- (void)appDidEnterBackground:(NSNotification *)notification {
    UIApplication  *application = [UIApplication sharedApplication];
    if([[UIDevice currentDevice] respondsToSelector:@selector(isMultitaskingSupported)])
    {
        NSLog(@"Multitasking Supported");
        if (background_task == UIBackgroundTaskInvalid) {
            background_task = [application beginBackgroundTaskWithExpirationHandler:^ {
                NSLog(@"Background task expiration\n");
                //Clean up code. Tell the system that we are done.
                [application endBackgroundTask: background_task];
                background_task = UIBackgroundTaskInvalid;
            }];
            //To make the code block asynchronous
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                //### background task starts
                NSLog(@"Running in the background\n");
                for(int cnt=0; cnt<10; cnt++) //while(TRUE)
                {
                    NSLog(@"Background time Remaining: %f",[[UIApplication sharedApplication] backgroundTimeRemaining]);
                    [NSThread sleepForTimeInterval:1]; //wait for 1 sec
                }
                //#### background task ends
                //Clean up code. Tell the system that we are done.
                NSLog(@"here you can do something before it really enters background");
                [NSThread sleepForTimeInterval:1]; //wait for 1 sec
                [application endBackgroundTask: background_task];
                background_task = UIBackgroundTaskInvalid;
            });
        }
        else
        {
            NSLog(@"Multitasking Not Supported");
        }
    }
}

You can know more from this slide.
I just do some simple useful modification from the snippet of this slide. Hope this helps.

Upvotes: 0

Himanshu Mahajan
Himanshu Mahajan

Reputation: 4799

In one of my apps, I was loading huge amount of data. I definitely can't expect the user to keep the app in foreground until the data is downloaded. I just use following code to get the data downloading while app is in background. Its working properly :-)
Please go through following steps:

1) use following line in header file of ViewController

   @property (nonatomic) UIBackgroundTaskIdentifier backgroundTask;

synthesis it in .m file.

2) in ViewDidLoad assign UIBackgroundTaskIdentifier like:

   self.backgroundTask = UIBackgroundTaskInvalid;

3) Use following line of code, here I am just keeping on getDataFromServer method inside beginBackgroundTaskWithExpirationHandler: block.

        self.backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{

        [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTask];
        self.backgroundTask = UIBackgroundTaskInvalid;
    }];

    /* Here your downloading Code, let say getDataFromServer method */

    [self getDataFromServer]; // Its dummy method

    /* Your downloading Code End Here */

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTask];
        self.backgroundTask = UIBackgroundTaskInvalid;
    });

4) If you want to check time remaining to download data in background, include following line in applicationDidEnterBackground:(UIApplication *)application delegate method of AppDelegate:

      NSLog(@"Background time remaining = %.1f seconds", [UIApplication sharedApplication].backgroundTimeRemaining);

Upvotes: 5

Sumit Sharma
Sumit Sharma

Reputation: 1867

AFNetworking allows you to perform heavy operation in background.

AFNetworking Library is a excellent combination of NSURLConnection and NSOperationQueue.This library is used for Asynchronous downloading in the background not affecting the other application.

AFNetworking allows you to handle the downloading with ExpirationHandle i.e. if anyhow during the downloading the connection is lost , it will again connect to it.

Library Source for AFNetworking

Reference Source AFNetworking works in Background

Apple Reference Link Apple

Background Execution and Multitasking Apple

Upvotes: 3

Saurabh
Saurabh

Reputation: 22873

Add below in your - (void)applicationDidEnterBackground:(UIApplication *)application

UIApplication  *app = [UIApplication sharedApplication];
UIBackgroundTaskIdentifier bgTask;

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

and you are good to go... I have this in one of my published download manager app

This will work just fine. You can also check how much time you have, since apple only enable 10 minutes background tasks. Use:

NSTimeInterval ti = [[UIApplication sharedApplication]backgroundTimeRemaining];
NSLog(@"backgroundTimeRemaining: %f", ti); // just for debug

Upvotes: 41

Related Questions