iOS HTTP request while in background - iphone

It is possible to make HTTP async requests to PHP server while the app being in background?
The app is a location based one, and should gather current location and send the coordinates to server every 5(or other value) minutes. Can I make the http posts to the server even the app is in background? I read lot of thoughts about this, but some of them told that can be done, others that can't be done.
Thanks,
Alex.

It can be done but it is unreliable because you ask the OS for time to send something and it can accept or deny your request. This is what I have (stolen from somewhere on SO):
[...] //we get the new location from CLLocationManager somewhere here
BOOL isInBackground = NO;
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground)
{
isInBackground = YES;
}
if (isInBackground)
{
[self sendBackgroundLocationToServer:newLocation];
}
- (void) sendBackgroundLocationToServer: (CLLocation *) lc
{
UIBackgroundTaskIdentifier bgTask = UIBackgroundTaskInvalid;
bgTask = [[UIApplication sharedApplication]
beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
}];
NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:2];
[dictionary setObject:[NSNumber numberWithDouble:lc.coordinate.latitude] forKey:#"floLatitude"];
[dictionary setObject:[NSNumber numberWithDouble:lc.coordinate.longitude] forKey:#"floLongitude"];
// send to server with a synchronous request
// AFTER ALL THE UPDATES, close the task
if (bgTask != UIBackgroundTaskInvalid)
{
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
}
}

These links will help you out...
iphone - Connecting to server in background

Related

UILocalNotification does not fire after 10 minutes in background

I have a VoIP application. Which is working fine. Call is working in foreground and background.
Following steps are done:
UIBackgroundModes => voip in Info.plist
Configured one of the app’s sockets for VoIP usage.
Before moving to the background, setKeepAliveTimeout:handler: is called
Configured audio session to handle transitions to and from active use.
To ensure a better user experience on iPhone, used the Core Telephony framework to adjust behavior in relation to cell-based phone calls;
To ensure good performance for VoIP app, used the System Configuration framework to detect network changes and allow app to sleep as much as possible.
Now the thing is when application is in background and a call comes then UILocalNotification fires for the following. And user can see a notification with two buttons CANCEL and RECEIVE
- (void)applicationDidEnterBackground:(UIApplication *)application
{
bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
[application endBackgroundTask: bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
dispatch_async(dispatch_get_main_queue(), ^{
while ([application backgroundTimeRemaining] > 1.0) {
NSString *friend = [self checkForIncomingChat];
if ([friend length]>0) {
[[UIApplication sharedApplication] cancelAllLocalNotifications];
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
if (localNotif) {
localNotif.alertBody = [NSString stringWithFormat: NSLocalizedString(#"%#", nil), friend];
localNotif.alertAction = NSLocalizedString(#"Receive", nil);
localNotif.soundName = #"alarmsound.caf";
localNotif.applicationIconBadgeNumber = 1;
[application presentLocalNotificationNow:localNotif];
friend = nil;
}
}
sleep(1);
}
[application endBackgroundTask:self->bgTask];
self->bgTask = UIBackgroundTaskInvalid;
});
}
- (NSString *) checkForIncomingChat {
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSString *incall = [prefs objectForKey:#"incall"];
if ([incall length]>0) {
[prefs setObject:#"" forKey:#"incall"];
return incall;
}
return #"";
};
Now the problem is:
After going to the background by pressing home button application fires UILocalNotification if any call comes within 10 minutes.
After 10 minutes if any call comes it is running in the background. UILocalNotification does not fire, so user can not know anything.
It happens because background task stops after 10 minutes.
How can I manage it or extend background task for long running or restart background task.
More and more answer I have found after searching but nothing works for long running background task.
Please anybody help me. I am trying it since 2 weeks.
It sounds like you have a VoIP socket that you are receiving the call on, so rather than looping and polling for call state, you can just present the local notification at the point that you read the data off the socket.
If the VoIP control socket is TCP, and marked with the appropriate ...NetworkServiceTypeVoIP key, your app will automatically get woken up from suspension for 10 seconds, at which point you can present the local notification.
See Configuring Sockets for VoIP Usage for more information.
Once that is done, all of the code that you shared above can be removed.

periodically download file from server and notify the app

Again I have a similar doubt as specified in the link setKeepAliveTimeout and BackgroundTasks.
I have to periodically take a file from server and accordingly provide local notification.
This has to be done when app is in background state.
Create backgoud task in your interface:
#property (nonatomic) UIBackgroundTaskIdentifier bgTask;
Than create method to backgroud working:
- (void) backgroundMethod {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//create backgriund task;
self.bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask: self.bgTask];
self.bgTask = UIBackgroundTaskInvalid;
}];
//do your stuff;
[[UIApplication sharedApplication] endBackgroundTask: self.bgTask];
self.bgTask = UIBackgroundTaskInvalid;
});
}
And call it whenewer you want periodicaly.

iOS background Location not sending http request

My app needs to track the users location in the background but it is failing to send a 'get' request. The http request gets sent immediately when the app comes to the foreground. I am using RestKit for all my network requests and I followed this tutorial to setup my background locations service.
In my applicationDidEnterBackground
-(void)applicationDidEnterBackground:(UIApplication *)application
{
self.bgLocationManager = [[CLLocationManager alloc] init];
self.bgLocationManager.delegate = self;
[self.bgLocationManager startMonitoringSignificantLocationChanges];
NSLog(#"Entered Background");
}
and I stopMonitoringSignificantLocationChange in my applicationDidBecomeActive delegate
This is my locationManager delegate where I accept the new updated location and send to my server
-(void) locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
NSLog(#"I am in the background");
bgTask = [[UIApplication sharedApplication]
beginBackgroundTaskWithExpirationHandler:
^{
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
}];
// ANY CODE WE PUT HERE IS OUR BACKGROUND TASK
NSString *currentLatitude = [[NSString alloc]
initWithFormat:#"%g",
newLocation.coordinate.latitude];
NSString *currentLongitude = [[NSString alloc]
initWithFormat:#"%g",
newLocation.coordinate.longitude];
NSString *webToken = [[NSUserDefaults standardUserDefaults] stringForKey:#"userWebToken"];
NSLog(#"I am in the bgTask, my lat %#", currentLatitude);
NSDictionary *queryParams;
queryParams = [NSDictionary dictionaryWithObjectsAndKeys:webToken, #"auth_token", currentLongitude, #"lng", currentLatitude, #"lat", nil];
RKRequest* request = [[RKClient sharedClient] post:#"/api/locations/background_update" params:queryParams delegate:self];
//default is RKRequestBackgroundPolicyNone
request.backgroundPolicy = RKRequestBackgroundPolicyContinue;
// AFTER ALL THE UPDATES, close the task
if (bgTask != UIBackgroundTaskInvalid)
{
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}
}
The network requests works as planned but it will not get called in the background. Is there any additional steps I need? In my info.plist I have the Required Background modes key and location-services as the value.
EDIT
I also referred to this past SO answer. I ran some tests with putting logs throughout the didUpdateToLocation call and they were all called but the 'get' request was not sent. Instead when I finally launch the app to the foreground it sent all the built of network requests (over 10).
EDIT (2)
I added RKRequestBackgroundPolicyContinue to my request but it did not change my results. (As you can see here in the background upload/download for restkit). I see Restkit initialize the host but fails to send the request until the app becomes active.
ANSWER
RestKit must be doing something that is prohibited in the background. Using an NSURLRequest works perfectly.
NSMutableURLRequest * urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"http://www.example.com/api/locations/background_update"]];
[urlRequest setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[urlRequest setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[urlRequest setHTTPMethod:#"POST"];
[urlRequest setHTTPBody:jsonData];
NSHTTPURLResponse *response = nil;
[NSURLConnection sendSynchronousRequest:urlRequest
returningResponse:&response
error:&error];
It is fine to use a synchronous request since there is no UI to disrupt with background tasks
Re-creating original suggestion as an answer
Have your try replacing your restKit calls with a stock synchronous NSURLConnection? – dklt Sep 20
I'm using exactly the same code as you and it works for me in RestKit. The only way I could make it work is ny creating a synchronous request (it doesn't make a lot of sense to do it asynchronously in this context anyway!). Please check this code and let us know if it works:
// REMEMBER. We are running in the background if this is being executed.
// We can't assume normal network access.
// bgTask is defined as an instance variable of type UIBackgroundTaskIdentifier
// Note that the expiration handler block simply ends the task. It is important that we always
// end tasks that we have started.
_bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:
^{
[[UIApplication sharedApplication] endBackgroundTask:_bgTask];
}];
// ANY CODE WE PUT HERE IS OUR BACKGROUND TASK
// For example, I can do a series of SYNCHRONOUS network methods (we're in the background, there is
// no UI to block so synchronous is the correct approach here).
NSNumber *latNumber = [NSNumber numberWithDouble:location.coordinate.latitude];
NSNumber *lngNumber = [NSNumber numberWithDouble:location.coordinate.longitude];
NSNumber *accuracyNumber = [NSNumber numberWithDouble:location.horizontalAccuracy];
NSDictionary *params = [NSDictionary dictionaryWithKeysAndObjects:#"lat",latNumber,#"lng",lngNumber,#"accuracy",accuracyNumber, nil];
RKURL *URL = [RKURL URLWithBaseURL:[NSURL URLWithString:SERVER_URL] resourcePath:#"/user/location/update" queryParameters:params];
RKRequest *request = [RKRequest requestWithURL:URL];
request.method = RKRequestMethodGET;
NSLog(#"Sending location to the server");
RKResponse *response = [request sendSynchronously];
if (response.isFailure)
NSLog(#"Unable to send background location, failure: %#", response.failureErrorDescription);
else {
NSError *error = nil;
NSDictionary *parsedBody = [response parsedBody:&error];
if (YES == [[parsedBody objectForKey:#"result"] boolValue]){
NSLog(#"Background location sent to server");
}
else {
//Something went bad
NSLog(#"Failed to send background location");
}
}
// AFTER ALL THE UPDATES, close the task
if (_bgTask != UIBackgroundTaskInvalid)
{
[[UIApplication sharedApplication] endBackgroundTask:_bgTask];
_bgTask = UIBackgroundTaskInvalid;
}
I'm almost sure the new thread spawned for your RKClient request is automatically killed after invoking it.
When you're application is running in the background you can finish a HTTP request you started before you entered the background but you cannot initiate a new request. You can only initiate certain network operations while in the background (voip, newsstand).

Using multiple beginBackgroundTaskWithExpirationHandler calls

I am trying to follow this previous post here: Best practice to send a lot of data in background on iOS4 device?
And basically, I have a method called getRequest that grabs information from the web server. There are about 50 pieces of data I need from the web server. So at the same time, I have 50 delegate calls to connectionDidFinishLoading. Currently my getRequest looks like:
-(void) getRequestWithURL:(NSString *) requestURL
{
static int getRequest = 0;
NSLog(#"getRequest: %i", getRequest);
getRequest++;
UIApplication *app = [UIApplication sharedApplication];
__block UIBackgroundTaskIdentifier taskID;
taskID = [app beginBackgroundTaskWithExpirationHandler:^{
NSLog(#"Time remaining: %f", app.backgroundTimeRemaining);
NSLog(#"Background task not completed");
[app endBackgroundTask:taskID];
}];
NSURLRequest *req = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:requestURL]];
NSURLConnection *con = [[NSURLConnection alloc] initWithRequest:req delegate:self] ;
[self startRequestWithConnection:con];
[req release];
if (taskID == UIBackgroundTaskInvalid) {
NSLog(#"Failed to create background task identifier");
}
}
Then in my connectionDidFinishLoading:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// process data from server
// endBackgroundTask:someTaskID???
}
I know you are allowed to have multiple calls of beginBackgroundTaskWithExpirationHandler, but I don't know if what I'm doing in my getRequest method is doing that since I only have one variable __block UIBackgroundTaskIdentifier taskID each time the method is called. And I'm also not sure if I need to call endBackgroundTask in the connectionDidFinishLoading method for each call to getRequest since you are supposed to balance the beginBackgroundTaskWithExpirationHandler with an endBackgroundTask: call. If so, how do I do that since my getRequest doesn't currently have that infrastructure? Do I need 50 ivars in order for the connectionDidFinishLoading method to see the 50 initial calls to getRequest? Thanks.
As you said, you need to balance beginBackgroundTaskWithExpirationHandler call with an endBackgroundTask call.
One solution I have in mind looks like this:
Create a new instance variable
UIBackgroundTaskIdentifier backgroundTaskID;
You are counting the requests anyway so you could also decrement getRequest in connectionDidFinishLoading:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// process data from server
getRequest--;
if (getRequest == 0 && backgroundTaskID != UIBackgroundTaskInvalid)
{
[[UIApplication sharedApplication] endBackgroundTask:backgroundTaskID];
backgroundTaskID = UIBackgroundTaskInvalid;
}
}
Now the background task gets ended after the last request has been completed. To start only one background task you start it in a method that gets called when the app goes to the background.
You need to listen for the UIApplicationDidEnterBackgroundNotification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationDidEnterBackground)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
and implement the method
- (void)applicationDidEnterBackground:(NSNotification *)notification
{
if (getRequest > 0) {
backgroundTaskID = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:backgroundTaskID];
backgroundTaskID = UIBackgroundTaskInvalid;
}];
}
}
Now you only have one running background task that starts automatically when your app goes to the background and you have running requests that gets ended when all your requests are done.
Another improvement would be to add your network requests to an NSOperationQueue to avoid the manual counting and limit the number of concurrent requests.
The work being done is simple whatever code comes next. The work isn't wrapped up into the Background task. The background task is just an id and a status that tells the iOS framework if you are finished doing your task or not. It's up to

Location Background - Network Connection

I am trying to post data on background and I want to receive response from in regular interval that so that I am trying to use CLLocation Manager . But didUpdateToLocation method calling only once.
Please suggest me !
enter code here
- (void)applicationDidEnterBackgroundUIApplication *)application
{
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone; // whenever we move
locationManager.desiredAccuracy = kCLLocationAccuracyBest; // 100 m
[locationManager startUpdatingLocation];
UIApplication* app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app 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
// code for Posting Data Here
NSLog(#"backgroundTimeRemaining: %f", [[UIApplication sharedApplication] backgroundTimeRemaining]);
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
}
- (void)locationManagerCLLocationManager *)manager
didUpdateToLocationCLLocation *)newLocation
fromLocationCLLocation *)oldLocation
{
NSLog(#"Location Change");
}
This Method Call Only Once.
Of course, and you should be happy what delegate received message at least once. There is no code which will delay application suspending in your background execution code. Also this kind of code can work about 10 minutes and will be killed by system.
If you need to track user location in background, than maybe you just set this mode for application background operation in Info.plist (UIBackgroundModes and add option location) and your app won't be suspended (only in case of heavy memory usage)