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.
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'm trying to send notifications in objective c but I can't figure out how.
So far I have
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
if(self.username != nil)
{
NSLog(#"Background fetch username: %#", self.username);
[self fetchNotifications];
}
completionHandler(UIBackgroundFetchResultNewData);
}
- (void)fetchNotifications
{
NSURL *url = [NSURL URLWithString:
[NSString stringWithFormat:#"%#%#",
#"https://myurl?username="
, self.username]];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response,
NSData *data, NSError *connectionError)
{
if (data.length > 0 && connectionError == nil)
{
NSDictionary *notifications = [NSJSONSerialization JSONObjectWithData:data
options:0
error:NULL];
int nbMessages = [[notifications objectForKey:#"NbOfUnreadMessage"] intValue];
int nbOfNewMatch = [[notifications objectForKey:#"NbOfNewMatch"] intValue];
int nbOfNewPlanification = [[notifications objectForKey:#"NbOfNewPlanification"] intValue];
//int nbMessages = [[notifications objectForKey:#"NbOfUnreadMessage"] intValue];
NSLog(#"Notifications - nbMessage: %i",nbMessages);
NSLog(#"Notifications - nbNewMatch: %i",nbOfNewMatch);
NSLog(#"Notifications - nbNewPlan: %i",nbOfNewPlanification);
// Schedule the notification
UILocalNotification* localNotification = [[UILocalNotification alloc] init];
localNotification.fireDate = nil;
localNotification.alertBody = #"test";
localNotification.alertAction = #"Show me the item";
localNotification.applicationIconBadgeNumber = [[UIApplication sharedApplication] applicationIconBadgeNumber] + 1;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
}];
}
When I try on the iPhone 6 simulator with IOS 8.1 I have the following error
Attempting to badge the application icon but haven't received permission from the user to badge the application
I will have to read and understand this post
but my real problem is when I use an iPhone 5 IOS 7.1 simulator, the function fetchNotifications is not even called.
Why fetchNotifications is only called under IOS 8.1?
performFetchWithCompletionHandler is called under both OS
Attempting to badge the application icon but haven't received permission from the user to badge the application
is the way Xcode tells you that you can't change the notifications badge value while running on the simulator. It's not allowed
This delegate method is called when iOS decide. From the UIApplicationDelegate documentation:
When an opportunity arises to download data, the system calls this method to give your app a chance to download any data it needs. More importantly, the system uses the elapsed time to calculate power usage and data costs for your app’s background downloads. If your app takes a long time to call the completion handler, it may be given fewer future opportunities to fetch data in the future.
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
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
});
}
}
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;
});
}