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
});
}
}
Related
I want to download many images around 300, and display it in the UI.
I have two questions:
If the app is in foreground, and suppose user sends it to background mode(by clicking on home button) then how do I assure the download to continue?
If the user force quits the app(doubleclick on home button and swipe the app from app switcher), then how do I ensure the app downloads the images?
I've been reading about background stuffs a lot. Few also say that in 2. case the download cannot continue.
Below are the links I referred:
http://www.appcoda.com/ios7-background-fetch-programming/
http://www.appcoda.com/background-transfer-service-ios7/
iOS Background downloads when the app is not active
I'm not getting the right approach to download the images in foreground, background/suspended, user force quits the app. I'm using AFNetworking for webservice calls.
Below is my code:
I've image details like the URL in .json file uploaded to amazon. To download the file I do,
[NSData dataWithContentsOfURL:[NSURL URLWithString:#"https://someurl/imageFile.json"]];
From this file I read the urls of images and download them along with image details. This is a big process. How Do I handle it? Please help..
Solution for your 1 question is below :
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.
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
NSURLSessionConfiguration *sessionConfig;
float timeout = 5 * 60.0f;
BOOL iOS8OrNew = [[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0;
if (iOS8OrNew) {
sessionConfig = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:identifier];
request.timeoutInterval = timeout;
}
else {
sessionConfig = [NSURLSessionConfiguration backgroundSessionConfiguration:identifier];
sessionConfig.timeoutIntervalForRequest = timeout;
}
sessionConfig.HTTPMaximumConnectionsPerHost = 10;
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:sessionConfig];
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request];
[manager setDidFinishEventsForBackgroundURLSessionBlock:^(NSURLSession * _Nonnull session) {
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
if (appDelegate.backgroundSessionCompletionHandler) {
void (^completionHandler)() = appDelegate.backgroundSessionCompletionHandler;
appDelegate.backgroundSessionCompletionHandler = nil;
completionHandler();
}
NSLog(#"All tasks are finished");
}];
Add below code in your AppDelegate:
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier
completionHandler:(void (^)())completionHandler {
self.backgroundSessionCompletionHandler = completionHandler;
//add notification
[self presentNotification];
}
-(void)presentNotification{
UILocalNotification* localNotification = [[UILocalNotification alloc] init];
localNotification.alertBody = #"Download Complete!";
localNotification.alertAction = #"Background Transfer Download!";
//On sound
localNotification.soundName = UILocalNotificationDefaultSoundName;
//increase the badge number of application plus 1
localNotification.applicationIconBadgeNumber = [[UIApplication sharedApplication] applicationIconBadgeNumber] + 1;
[[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
}
For your 2 solution
If the system kills your app and your background session has active downloads, your downloads will continue and the system will launch your app when the downloads complete. However, if a user force quits your app, all tasks get cancelled.
i need to get and send location to server even if application is terminated, i found solution how to get location, but how i can send it to server - i cant found. I think i can send it with background task, but how it together work, i can't imagine, i try with this code indidFinishLaunchingWithOptions:
if ([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey]) {
// This "afterResume" flag is just to show that he receiving location updates
// are actually from the key "UIApplicationLaunchOptionsLocationKey"
self.shareModel.afterResume = YES;
[self.shareModel startMonitoringLocation];
[self.shareModel addResumeLocationToPList];
UIApplication *application = [UIApplication sharedApplication];
__block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://google.com"]];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[[NSUserDefaults standardUserDefaults] setObject:str forKey:#"DOWNLOADTASK"];
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
}
but than i print NSUserDefaults its empty by this key, what i doing wrong, may be i'm using the wrong background task ??
How to Get Location Updates for iOS 7 and 8 Even when the App is Suspended
https://github.com/voyage11/GettingLocationWhenSuspended
iOS 9 how get locations even if app terminated
According to the apple in the document about the description of the background, any app background task execution time of 10 minutes.After 10 minutes, the app will be iOS forced to hang up.
But there are five types of app allows for "unlimited" background running time.
Audio。
Location/GPS。
VoIP。
Newsstand。
Exernal Accessory 。
You can take any app statement for the above five types in order to obtain the background of infinite time, but when you submit the app to the app Store, apple will review your app, once found you "abuse" of the backend API, your app will be rejected.
However, in the case of enterprise development, there is no "abuse" problem - enterprise app can through OTA deployment, not through the apple store review.
In enterprise deployment, you can be a statement for VoIP app, but the program simply has nothing to do with VoIP, we only for the purpose of the iOS give us infinite background execute permissions.Declaration process is in the app Info. The file to add the following key:
In the first place in the file the Required background modes that add the following two: in a App play audio or streams audio/video using AirPlay and App provides Voice over IP services.
UIBackgroundModes
voip
I tested the following code:
- (void)backgroundHandler {
NSLog(#"### -->backgroundinghandler");
UIApplication* app = [UIApplicationsharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
}
Through the test, I have received an "infinite" background execution time.I don't know how long do you think is "infinite", but in this case, the background task to run at least 55 hours, until I lose patience to stop testing.
my english is very poor,this blogs are reprinted!
You can't perform any task in suspended or terminated state as per apple's guideline. you can get location updates in background state but can't do it when app is terminated or suspended. Refer this link and this tutorial for background location fetch. hope this will help :)
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.
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;
});
}
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]