Continue download in background - ios

I am creating an application wherein I am downloading some data from server. While going in background I want that connection should continue running so that data can be downloaded. I know there is method in appDelegate
- (void)applicationDidEnterBackground:(UIApplication *)application
which is called when application enters background. But as the connection is created in viewController, how can it be managed in appDelegate?
Also is/are there other way(s) this can be done? I have gone through this link but is there a something simple to implement?

One way to do some operations that continue in the background is to create a separate thread to do the downloading. Inside the thread, bracket your download operations between calls to beginBackgroundTaskWithExpirationHandler: and endBackgroundTask. You don't need to check to see whether you are running in the background or not, you just always call these two methods.
// Tell iOS this as a background task in case we get backgrounded
UIBackgroundTaskIdentifier taskId = [[UIApplication sharedApplication]
beginBackgroundTaskWithExpirationHandler:NULL];
//----------------------------------------------
// Perform your download operations here
//----------------------------------------------
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
// Tell iOS that we are done with stuff that needed to keep going even if backgrounded
[[UIApplication sharedApplication] endBackgroundTask:taskId];

[edit]
Sorry I was incorrect, as was pointed out in the comments you can extend the time limit you have to perform operations once/before your app goes into the background. Here is Apple's Official Documentation

I don't know how you handle your data downloading exactly. But you can take a look at ASIHTTPRequest. It is very simple and straightforward, and works with ARC if you set the compiler flags to -fno-objc-arc. With this you only have to use
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setShouldContinueWhenAppEntersBackground:YES]; //For iOS 4.0 and up
And that works.
Here you can see how ASIHTTPRequest works
Hope it helps!

Related

Continue a NSURLConnection after the App has been terminated

I would like to know how it is possible to continue a async NSURLConnection, which has been started in the foreground, when the app will be terminated.
Currently I am starting a NSURLConnection when the app goes in the background. This works fine as long as the user is slower than the connection, when he wants to terminate the app. But when the user is quicker than it, the connection can't be established.
Here is my code:
// AppDelegate.m
- (void)applicationDidEnterBackground:(UIApplication *)application
{
AnObject *newObject = [[AnObject alloc] init];
[newObject InactiveApp];
}
// AnObject.m
- (void)InactiveApp
{
self.backgroundTaskID = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL];
// setting up the NSURLRequest
// [...]
dispatch_async(dispatch_get_main_queue(), ^
{
[NSURLConnection connectionWithRequest:request delegate:self];
});
}
// delegate functions, endBackgroundTask-closing, etc. is following
Unfortunately this is not working and I would like to know whether someone knows another way to fix it. There has to be a way which is similar like Snapchat or WhatsApp is doing it, because when you write an message and terminate the app right after pressing send, the message will be delivered.
The only way I could imagine is to do it with a background fetch but I think that is not the best solution, due to the fact that I just want to make one single connection when the App is send to the background.
I agree with Andy, that you should pursue NSURLSession and a background NSURLSessionConfiguration. See downloading content in the background section of the App Programming Guide for iOS: Background Execution.
By the way, the idea in your question will work fine (especially if you need support for iOS versions prior to 7.0, where NSURLSession and its background sessions are not available). Two observations regarding your code snippet:
The way you've written it, would appear that your AnObject would be prematurely deallocated when it falls out of scope and your app would therefore fail when it tried to call the delegate methods. Make sure to maintain a strong reference to AnObject.
Don't forget to call endBackgroundTask when the download is done. Likewise (and more subtly), the timeout handler should end the background task, too. See the Executing Finite Length Task section of the aforementioned App Programming Guide.
By the way, you mention requests continuing after the app is terminated. If a user manually terminates an app, that kills both background tasks contemplated in your question as well as background NSURLSession tasks. These are intended to gracefully handle continuing tasks if the app leaves foreground, not if the user manually terminates the app. The NSURLSession approach gracefully handles terminations due to memory pressure, but not manual termination.

AFNetWorking executes setCompletionBlock after 30 seconds if app is inactive

I am using AFNetworking for download of files in one of my project.
Everything is working properly for download if my app is awake.
The error occurs in below scenario.
I keep for download.
I navigate away from app for 30+ seconds
When I come back, it executes setCompletionBlock and because of this my download is incomplete.
If I comes back to app before 30 seconds, download continues.
Any idea how to solve this issue?
I tried setting timeout interval for NSURLRequest to 300, but still it execute setCompletionBlock after 30 seconds.
NSURLRequest *request = [NSURLRequest requestWithURL:
[NSURL URLWithString:currentURL] cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:300];
operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation start];
Two options:
See the Executing a Finite-Length Task in the Background section of the iOS App Programming Guide:App States and Multitasking. Basically, before you start your network request, inform the OS that you want to request additional time to complete this network request if the app happens to go into the background:
UIApplication *application = [UIApplication defaultApplication];
bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
// Clean up any unfinished task business by marking where you
// stopped or ending the task outright.
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
This assumes you have some instance variable or class property for that bgTask:
UIBackgroundTaskIdentifier bgTask;
Anyway, inside the network request's completion block, end the background task:
if (bgTask != UIBackgroundTaskInvalid) {
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
If you do the above, the app will be allowed to have a few minutes to complete the network request when the app enters the background. Note, if you have multiple requests running in the background, you will presumably want a separate UIBackgroundTaskIdentifier for each request.
In iOS 7.0 and later, you have NSURLSession, which permits a much richer background network request mechanism. AFNetworking offers AFURLSessionManager as an interface for NSURLSession, but it's nowhere as robust as AFHTTPRequestManager.
See the Background Transfer Considerations section of the URL Loading System Programming Guide: Using URLSession. Basically, you need to:
Create NSURLSessionConfiguration using backgroundSessionConfiguration: (in iOS 8, it's backgroundSessionConfigurationWithIdentifier:).
Issue only NSURLSessionDownloadTask or NSURLSessionUploadTask requests.
Use the delegate-based implementation of those NSURLSessionTask factory methods.
Make sure to implement handleEventsForBackgroundURLSession: in your app delegate, saving the completionHandler.
Implement a URLSessionDidFinishEventsForBackgroundURLSession method in your NSURLSessionDelegate object.

AFNetworking 2 and background tasks

I have a question about AFNetworking 2 and background downloads/uploads thanks to the new iOS7 NSURLSession background requests
Is this automatically handled by my AFHTTPRequestOperationManager ? Does it automatically set my requests'session to background mode?
I saw that the AFURLSessionManager Has a setDidFinishEventsForBackgroundURLSessionBlock Method but I wonder if everything is automatic?
If my app is killed or suspended, will requests keep on going? How can I get a callback when my app is relaunched?
Thanks a lot for your help!
AFHTTPRequestOperationManager uses the old NSURLConnection so that doesn't facilitate background downloading.
AFURLSessionManager uses NSURLSession under the hood so that does. I think you still need to configure the NSURLSession appropriately.
"The NSURLSession class supports background transfers while your app is suspended. Background transfers are provided only by sessions created using a background session configuration object (as returned by a call to backgroundSessionConfiguration:)."
Suggested reading:URL Loading System

Why isn't UIWebView loading an NSURLRequest at applicationWillTerminate: in my app delegate?

I am trying to clean up when the app is about to terminate/enter the background.
But, when the following code is called,
- (void)applicationWillTerminate:(UIApplication *)application
{
NSURL* url = [NSURL URLWithString:#"http://www.mysite.com/mobile/home?sysmethod=proclogout&systype=mobile&pagetype=jquerymobile"];
[self.webView loadRequest:[[NSURLRequest alloc] initWithURL:logoutURL]];
}
... the request starts loading, but never finishes. I read in Apple docs that apps have ~ 5 seconds to finish what they're doing once applicationWillTerminate: is called.
I don't believe the process is taking anywhere near that long, since I load this same request elsewhere in the app, and it always happens in mere milliseconds.
I have tried this both synchronously and asynchronously, with no luck either way.
Thanks for any thoughts!
Request loading in a UIWebView is asynchronous. That explains why the request begins to load but never finishes. If you want to make sure that a certain URL is called or loaded, you will have to somehow keep the applicationWillTerminate: method from exiting. If you don't need to really load the request in a web view, you could do use [NSURLConnection sendSynchronousRequest:returningResponse:error:] instead.
Also, a kind of hacky way to do it might be this:
[_webview stringByEvaluatingJavaScriptFromString:#"window.location.href=\"http://example.com/logout\""];
stringByEvaluatingJavaScriptFromString: is a blocking, synchronous method. I haven't tried, however, if it would really only return once the request is fully loaded. It might already return just after setting the location.

Running a long process in the background in iOS5 and above

I have have an download queue running using ASIHTTP request. When the user presses the home screen and the app goes into the background I would like this operation to continue. I know that ASIHTTP request can run in the background, but I want the process that runs the ASIHTTP request to run in background as well.
How do i do it?
I saw this post in StackOverflow: Continuing a long running process in the background under iOS4
But the solution is in iOS4. I would like to do it in iOS5 and above..
The solution given:
UIApplication *app = [UIApplication sharedApplication];
if ([app respondsToSelector:#selector(beginBackgroundTaskWithExpirationHandler:)]) {
backgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{
if (backgroundTaskIdentifier != UIBackgroundTaskInvalid)
{
// you took too long - clean up what you can, then …
[app endBackgroundTask:backgroundTaskIdentifier];
backgroundTaskIdentifier = UIBackgroundTaskInvalid;
}
});
}];
}
Another question is where should I put this code.
Need some guidance on this..
The solution you found in the other answer is good for iOS 4.0 and above. It works just fine, and remains the recommended way to perform an extended task when moving to the background.
Also, unless you're trying to support iOS 3.x -- which isn't all that advisable anymore -- you don't need the respondsToSelector check.

Resources