AFNetWorking executes setCompletionBlock after 30 seconds if app is inactive - ios

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.

Related

Writing background service in ios

Can we write background service in ios which will continuously run in background after some delay (e.g 1 min).
I have an application which fetch data after every minute i want to write background service for it.
independent of application running or not,though application is not in memory, service should continuously run after 1 minute.
I have written same service in android application but i don't know it is possible or not in ios.
No you can't, iOS decides when your app can fetch.
check the "Background Fetch mode" in this article.
Well, Actualy it depend on task what you want to perform. Main point is you cant perform any task in Background permanently. at the interval of time you need to request.
You can perform few task in background like Downloading and etc. You need to use dispatch_async Something like in - (void)applicationDidEnterBackground:(UIApplication *)application
- (void)applicationDidEnterBackground:(UIApplication *)application
{
bgTask = [application beginBackgroundTaskWithName:#"MyTask" expirationHandler:^{
// Clean up any unfinished task business by marking where you
// stopped or ending the task outright.
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do the work associated with the task, preferably in chunks.
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
}
For more Detail like finite task , you can find on apple's document in below link.
BackgroundExecution
You can also get more help regarding background services from below link.
running-background-services-in-ios

Sending a HTTP request when app closes

So I am trying to do a simple POST request when my app closes down.
I've tried [NSURLConnection sendAsynchronousRequest]
and doing [NSURLConnection sendSynchronousRequest] with dispatch_async. The only thing that actually works as I want is to do the synchronous request on the main thread, but then it lags, especially if the server is slow to respond.
Both kind of work, except they send the actual request when the app opens again, instead of when it closes. I am currently doing this in applicationDidEnterBackground but I've tried applicationWillResignActive also.
I have set the Application does not run in background in the apps info.plist also. No change.
I could do all things when the app opens. But the code will be nicer if I can achieve it when closing the application.
Is it possible?
From the documentation for applicationDidEnterBackground -
it's likely any background tasks you start in
applicationDidEnterBackground: will not run until after that method
exits, you should request additional background execution time before
starting those tasks. In other words, first call
beginBackgroundTaskWithExpirationHandler: and then run the task on a
dispatch queue or secondary thread.
So, you are requesting an asynchronous operation, but this task won't execute before applicationDidEnterBackground returns, and once this method returns your application will no longer be active. The tasks are sitting there and run once your app returns to the foreground.
The iOS programming guide provides advice on executing a task when your app moves to the background
You will need something like -
- (void)applicationDidEnterBackground:(UIApplication *)application
{
bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
// Clean up any unfinished task business by marking where you
// stopped or ending the task outright.
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do the work associated with the task
[NSURLConnection sendSynchronousEvent....];
// TODO process results..
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
}

Implementing long running tasks in background IOS

I have been working on an app in which user can record video using AVFoundation and send to the server, video has maximum size up to 15M, depending on the internet speed & type it can take from 1 to 5 minutes approximately to transfer video to the server. I am transferring the recorded video to the server in the background thread so that user can continue other stuff on the app while video is being uploaded to the server.
While reading the Apple Docs for implementing long running tasks in backround, I see that only few kinds of apps are allowed to execute in the background.
e.g.
audio—The app plays audible content to the user while in the background. (This content includes streaming audio or video content using AirPlay.)
Does it qualify my app also for running the tasks in the background? or I need to transfer the video on the main thread?
NSOperationQueue is the recommended way to perform multi-threaded tasks to avoid blocking the main thread. Background thread is used for tasks that you want to perform while your application is inactive, like GPS indications or Audio streaming.
If your application is running in foreground, you don't need background thread at all.
For simple tasks, you can add a operation to a queue using a block:
NSOperationQueue* operationQueue = [[NSOperationQueue alloc] init];
[operationQueue addOperationWithBlock:^{
// Perform long-running tasks without blocking main thread
}];
More info about NSOperationQueue and how to use it.
The upload process will continue while in background, but your application will be eligible to be suspended, and thus the upload may cancel. To avoid it, you can add the following code to application delegate to tell the OS when the App is ready to be suspended:
- (void)applicationWillResignActive:(UIApplication *)application {
bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
// Wait until the pending operations finish
[operationQueue waitUntilAllOperationsAreFinished];
[application endBackgroundTask: bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
}
From your response to Dwayne, you do not need to be able to download in background mode. Rather what you need is to do your download in another thread (background thread) beside main thread. Something like this for GCD:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do you download here...
});
Your requirement qualifies to run in background. You do not need to register any background modes supported in the info plist. All you need to do is, when the app is about to go into background, request for additional time using background task handler and perform your task in that block. Make sure you stop your handler before 10 mins so as to not get force terminated by the OS.
You may use the below code from Apple.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
// Clean up any unfinished task business by marking where you
// stopped or ending the task outright.
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do the work associated with the task, preferably in chunks.
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});}

Continue download in background

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!

iOS application executing tasks in background

I was wondering if I could send some webservice calls while my application is in the background. How does skype do it? Even if I press the home button my call stays connected.
Building on what rckoenes stated, applications are allowed to register background tasks to be completed after the user hits the home button. There is a time limit of 10 or 15 minutes for these tasks to complete. Again, you can register a task to complete immediately after the user hits home, this does NOT allow you to execute code say an hour after they exit the app.
UIApplication* app = [UIApplication sharedApplication];
task = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:task];
task = UIBackgroundTaskInvalid;
}];
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do the work associated with the task.
NSLog(#"Started background task timeremaining = %f", [app backgroundTimeRemaining]);
if (connectedToNetwork) {
// do work son...
}
[app endBackgroundTask:task];
task = UIBackgroundTaskInvalid;
});
UPDATE: if your app supports versions of iOS previous to iOs 4, you should also check to ensure that multitasking is supported before registering a background task. Use something along the lines of:
UIDevice* device = [UIDevice currentDevice];
BOOL backgroundSupported = NO;
if ([device respondsToSelector:#selector(isMultitaskingSupported)])
backgroundSupported = device.multitaskingSupported;
Try This... Excellent code for running app in background with no time limit. (I tested it for downloading more than 600 mb data from web-service.)
- (void)applicationDidEnterBackground:(UIApplication *)application
{
UIApplication *app = [UIApplication sharedApplication];
UIBackgroundTaskIdentifier bgTask;
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
}];
}
Update ::
you can found more information regarding multitaksing in this apple doc Background Execution.
Please test on device.
It depends or what kind of application are you trying to code.
Skype is registered as a VoIP (Long-running app) app and this is why it can stay "running" although it is on the background.
Apple separates apps in three:
Executing Finite-Length Tasks (you can run tasks for a finite amount of time)
Downloading Content in the Background (you can download content to present it to the user when the app becomes active again)
Implementing Long-Running Tasks (This is the most interesting background apps category, with some subcategories that the developer should define for your app)
Apps that play audible content to the user while in the background, such as a music player app
Apps that record audio content while in the background
Apps that keep users informed of their location at all times, such as a navigation app
Apps that support Voice over Internet Protocol (VoIP) (SKYPE is here)
Apps that need to download and process new content regularly
Apps that receive regular updates from external accessories
So, you need to evaluate in which category your app is and what your service operation performs. Maybe if you're sending some small things to the service the best approach is only to request some extra time on the background for doing the job.
More info about all of this are on this link:
https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html

Resources