iOS 4.0 Task Completion in Background - ios

Using iOS 4.0 I am trying to do a download an image in the background when the app is suspended. What I am doing is when I get the applicationDidEnterBackground delegate call, I initiate one asynchronous NSURLConnection and set the app delegate as the delegate for the connection. But none of the NSURLConnection delegates are getting called back. I have captured the network calls using Wireshark and I can see that my request succeeded and got the response too. But since none of the delegate methods are invoked I am unable to do anything with the data.
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(#"Application entered background state.");
// UIBackgroundTaskIdentifier bgTask is instance variable
NSAssert(self->bgTask == UIBackgroundTaskInvalid, nil);
bgTask = [application beginBackgroundTaskWithExpirationHandler: ^{
dispatch_async(dispatch_get_main_queue(), ^{
[application endBackgroundTask:self->bgTask];
self->bgTask = UIBackgroundTaskInvalid;
});
}];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://animal.discovery.com/mammals/leopard/pictures/leopard-picture.jpg"] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:15];
self.connection = [[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO] autorelease];
[self.connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[self.connection start];
[request release];
NSLog(#"Sent download request....");
dispatch_async(dispatch_get_main_queue(), ^{
while([application backgroundTimeRemaining] > 1.0) {
//Do something..
}
[application endBackgroundTask:self->bgTask];
self->bgTask = UIBackgroundTaskInvalid;
NSLog(#"Ending background task....");
});
}
What should I do to complete a download asynchronously in background when the application goes to background?

I think it's because you're ending the background task before the download actually finishes. You should put the call to [application endBackgroundTask:self->bgTask]; in the NSURLConnectionDelegate method when the download finishes (both success and unsuccessfully), and not just after calling [self.connection start]

Related

Web service call in background mode - iOS

I need to call a web service in every minute and parse the data when app is in background state.
Since the APP uses location service I have enabled background mode for update Location.
I tried calling location update by using a timer background task, but it not working.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
self.bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
NSLog(#"ending background task");
[[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
self.bgTask = UIBackgroundTaskInvalid;
}];
self.timer = [NSTimer scheduledTimerWithTimeInterval:60
target:self.locationManager
selector:#selector(startUpdatingLocation)
userInfo:nil
repeats:YES];
}
Is there any way to implement this with less battery consumption.
I referred this link
I'm not getting which solution is better here.
AppDelegate.h
#import <UIKit/UIKit.h>
#interface AppDelegate : NSObject {
// Instance member of our background task process
UIBackgroundTaskIdentifier bgTask;
}
#end
AppDelegate.m
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(#"Application entered background state.");
// bgTask is instance variable
NSAssert(self->bgTask == UIBackgroundTaskInvalid, nil);
bgTask = [application beginBackgroundTaskWithExpirationHandler: ^{
dispatch_async(dispatch_get_main_queue(), ^{
[application endBackgroundTask:self->bgTask];
self->bgTask = UIBackgroundTaskInvalid;
});
}];
dispatch_async(dispatch_get_main_queue(), ^{
if ([application backgroundTimeRemaining] > 1.0) {
// Start background service synchronously
[[BackgroundCleanupService getInstance] run];
}
[application endBackgroundTask:self->bgTask];
self->bgTask = UIBackgroundTaskInvalid;
});
}
There are couple key lines in the above implementation:
The first is the line bgTask = [application beginBackgroundTaskWithExpirationHandler..., which requests additional time to run clean up tasks in the background.
The second is the final code block of the delegate method beginning with dispatch_async. It's basically checking whether there's time left to run an operation via the call [application backgroundTimeRemaining]. In this example, I'm looking to run the background service once but alternatively, you can use a loop checking on the backgroundTimeRemaining on each iteration.
The line [[BackgroundCleanupService getInstance] run] will be a call to our singleton service class, which we'll build right now.
With the app delegate ready to trigger our background task, we now need a service class that will communicate with the web server. In the following example, I'm going to a post a fictitious session key and parse a JSON encoded response. Also, I'm using two helpful libraries to make the request and deserialize the returned JSON, specifically JSONKit and ASIHttpRequest.
BackgroundCleanupService.h
#import <Foundation/Foundation.h>
#interface BackgroundCleanupService : NSObject
+ (BackgroundCleanupService *)getInstance;
- (void)run;
#end
BackgroundCleanupService.m
#import "BackgroundCleanupService.h"
#import "JSONKit.h"
#import "ASIHTTPRequest.h"
#implementation BackgroundCleanupService
/*
* The singleton instance. To get an instance, use
* the getInstance function.
*/
static BackgroundCleanupService *instance = NULL;
/**
* Singleton instance.
*/
+(BackgroundCleanupService *)getInstance {
#synchronized(self) {
if (instance == NULL) {
instance = [[self alloc] init];
}
}
return instance;
}
- (void)run {
NSURL* URL = [NSURL URLWithString:[NSString stringWithFormat:#"http://www.example.com/user/%#/endsession", #"SESSIONKEY"]];
__block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:URL];
[request setTimeOutSeconds:20]; // 20 second timeout
// Handle request response
[request setCompletionBlock:^{
NSDictionary *responseDictionary = [[request responseData] objectFromJSONData];
// Assume service succeeded if JSON key "success" returned
if([responseDictionary objectForKey:#"success"]) {
NSLog(#"Session ended");
}
else {
NSLog(#"Error ending session");
}
}];
// Handle request failure
[request setFailedBlock:^{
NSError *error = [request error];
NSLog(#"Service error: %#", error.localizedDescription);
}];
// Start the request synchronously since the background service
// is already running on a background thread
[request startSynchronous];
}
#end
may be helped

How to get Web service response in an iOS app when application is in Background?

I am working on an iOS application in which I am using Restful web service by NSURLConnection, when I call web service and after calling web service press home button then application goes in to background and it is not getting response in background. In my application response should get even when application will be in background state.
So please suggest me any suitable answer for this.
Should I use NSURLSession ?
NSURL *myURL = [NSURL URLWithString:#""]; // set your url here
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDownloadTask *getTask = [session downloadTaskWithURL:myURL completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
// do stuff with the result
}];
// don't forget to start your task
[getTask resume];
Here's a nice tutorial you might want to take:
http://www.raywenderlich.com/51127/nsurlsession-tutorial
Use UIBackgroundTaskIdentifier, something like this
(void)applicationDidEnterBackground:(UIApplication *)application {
//Start Background Service and get data every 10 second
[self runBackgroundTask:10];
}
(void)runBackgroundTask: (int) time{
UIApplication *app;
app = [UIApplication sharedApplication];
//check if application is in background mode
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
//create UIBackgroundTaskIdentifier and create tackground task, which starts after time
__block UIBackgroundTaskIdentifier bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
dispatch_async(dispatch_get_main_queue(), ^{
NSTimer *refreshTimer = [NSTimer scheduledTimerWithTimeInterval:time target:self selector:#selector(doRefresh) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:refreshTimer forMode:NSDefaultRunLoopMode];
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
}
}
doRefresh - This is method where you give your web service

UILocalNotification dosen't prompts after 10 mins in background

In didFinishLaunchingWithOptions a timer loop calling a function httpRequest every 1 minute interval.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//rest of code
NSTimer *notifyTimer = [NSTimer timerWithTimeInterval:60 target:self selector:#selector(httpRequest) userInfo:nil repeats:YES];//7200.0
[[NSRunLoop mainRunLoop] addTimer:notifyTimer forMode:NSDefaultRunLoopMode];
return YES;
}
After pressing home button application is going to background and calling function applicationDidEnterBackground so a background task is starting.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
__block UIBackgroundTaskIdentifier bgTask;
UIApplication *app = [UIApplication sharedApplication];
expirationHandler = ^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
bgTask = [app beginBackgroundTaskWithExpirationHandler:expirationHandler];
};
bgTask = UIBackgroundTaskInvalid;
bgTask = [app beginBackgroundTaskWithExpirationHandler:expirationHandler];
}
By httpRequest function I am geting Y from web server after every 1 minute interval so a UILocalNotification fires after every seconds.
-(NSString *)httpRequest {
NSURL *url = [NSURL URLWithString:#"http://192.168.10.67/t.php"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
NSString *userAgent = [NSString stringWithFormat:#"bgTaskTest-IOS"];
[request setValue:userAgent forHTTPHeaderField:#"User-Agent"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"content-type"];
[request setHTTPMethod:#"GET"];
[request setTimeoutInterval:25];
NSURLResponse *response;
NSData *dataReply = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
NSString *stringReply = [[NSString alloc] initWithData:dataReply encoding:NSASCIIStringEncoding];
if ([stringReply isEqualToString:#"Y"]) {
[self showLocalNotification:nil]; //calling UILocalNotification
} else {
NSLog(#"%#",stringReply);
}
return stringReply;
}
Function showLocalNotification is calling after every 1 minute based on response of httpRequest function.
-(void)showLocalNotification {
NSString *msg = #"test message";
[[UIApplication sharedApplication] cancelAllLocalNotifications];
UILocalNotification *_localNotification = [[UILocalNotification alloc]init];
_localNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:1];
_localNotification.timeZone = [NSTimeZone defaultTimeZone];
_localNotification.alertBody = msg;
_localNotification.soundName = UILocalNotificationDefaultSoundName;
_localNotification.applicationIconBadgeNumber = [[UIApplication sharedApplication] applicationIconBadgeNumber]+1;
[[UIApplication sharedApplication] scheduleLocalNotification:_localNotification];
//[[UIApplication sharedApplication] presentLocalNotificationNow:_localNotification];
}
Everything is right, notification prompts after every 1 munite when application is in background.
But my problem is Background Task's life time is 10 mins, so after 10 mins no notification prompts. For this reason I am starting Background task again in beginBackgroundTaskWithExpirationHandler but my application kill at this time of restarting background task.
I couldn't able to use notification more than 10 mins when application is in background.
Please anybody help me.
There is no way (within the app store guidelines) to run arbitrary code in the background for longer than ten minutes (as you have noticed).
After 10 minutes your app will be suspended. There is a couple of ways around this, registering for other background modes (such as background audio, playing a silent sound file continuously) or background voip or background location services.
These hacky work around will keep your application unsuspended however your application will not get approved for the store.
in iOS7 there are advances to running code in the background, however nothing that will do what you want.
So if this is an app for your own use, use private API's or the method I suggested above, however if you want to get this app on the store, I'm afraid your out of luck.

Killing connections as app enters background

I have numerous UIWebViews in my iOS (versions 5-6.2) application. When the app enters the background, everything runs smoothly. However, when it enters the foreground after ~10 minutes, I get an error message either saying something like "a valid hostname cannot be found" or "connection timeout."
I am assuming this has to do with my lack of action towards these UIWebViews when applicationDidEnterBackground: gets called. How would I be able to kill these connections? I understand I need to use notification center, but unlike previous questions like this, I am using ARC so there's no dealloc method in which I can remove observers.
EDIT
Here is some of my web view code:
WebViewController.m
NSURLRequest *request = [NSURLRequest requestWithURL:urlToLoad cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30.0f];
// load the request to the UIWebView _webView
[_webView loadRequest:request];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:TRUE];
if (connection) {
receivedData = [NSMutableData data];
}
Any help is greatly appreciated. Thank you.
if you are performing some Important or Precious Downloading or Uploading operation and your app enters background, in that case you can request for Additional time from 'IOS' to finish your work, it grants additional 10 minutes to finish that while your app is in background mode.
But Keep in Mind that, your Operation must be important and Acceptable, otherwise your app may get Rejected from Apple Review Process.
For More Details Please Refer Apple Document for Background Execution and Multitasking
Now, Concluding my Point and time for some Action, to Continue your Task in Background you can perform with following method, no need to manage Application Delegate method. just use following snippet and dont use delegate in that for downloading or uploading.
if ([[UIDevice currentDevice] respondsToSelector:#selector(isMultitaskingSupported)]) { //Check if our iOS version supports multitasking I.E iOS 4
if ([[UIDevice currentDevice] isMultitaskingSupported]) { //Check if device supports mulitasking
UIApplication *application = [UIApplication sharedApplication]; //Get the shared application instance
__block UIBackgroundTaskIdentifier background_task; //Create a task object
background_task = [application beginBackgroundTaskWithExpirationHandler: ^ {
[application endBackgroundTask: background_task]; //Tell the system that we are done with the tasks
background_task = UIBackgroundTaskInvalid; //Set the task to be invalid
//System will be shutting down the app at any point in time now
}];
//Background tasks require you to use asyncrous tasks
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//Perform your tasks that your application requires
NSLog(#"\n\nRunning in the background!\n\n");
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"YOUR HOST URL"]];
NSURLResponse *response = nil;
NSError *requestError = nil;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&requestError];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSLog(#"ResponseString:%#",responseString);
[application endBackgroundTask: background_task]; //End the task so the system knows that you are done with what you need to perform
background_task = UIBackgroundTaskInvalid; //Invalidate the background_task
});
}
}

ios 5 NSURLConnection and block download in standby mode

I'm using NSURLConnection to download content from a server (and I'm working on an iPad application in iOS 5.0).
I wish the NSURLConnection continue downloading even when the iPad goes on standby.
is it possible?
This is my code:
-(void)startDownload {
UIDevice* device = [UIDevice currentDevice];
BOOL backgroundSupported = NO;
if ([device respondsToSelector:#selector(isMultitaskingSupported)])
backgroundSupported = device.multitaskingSupported;
NSLog(#"\n\nbackgroundSupported= %d\n\n",backgroundSupported);
dispatch_async(dispatch_get_main_queue(), ^ {
NSURLRequest *req = [[NSURLRequest alloc] initWithURL:imageURL];
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:req delegate:self startImmediately:NO];
[conn scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[conn start];
if (conn) {
NSMutableData *data = [[NSMutableData alloc] init];
self.receivedData = data;
}
else { ... }
}) ;
}
Thanks!
Every app can continue to execute in the background for roughly 10 minutes before it terminates. Only certain apps can continue to execute in the background, such as audio/gps/bluetooth etc related apps. You can find out more at Background Execution and Multitasking (under App States and Multitasking section to the left).
The following code sample is from the app doc and can help you get started so your connection can last up to about 10 minutes -
- (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;
});
}

Resources