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)
Related
Is it possible to send information about the location of the iPhone to a separate service, though the application is running in the background or as a background service?
You need to call webservice in CLLocationManagerDelegate Method see this e.g. below
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
CLLocationCoordinate2D coordinate = [newLocation coordinate];
CLLocationCoordinate2D oldCoordinate = [oldLocation coordinate];
CLLocation *newlocation = [[CLLocation alloc] initWithLatitude:coordinate.latitude longitude:coordinate.longitude];
CLLocation *oldlocation = [[CLLocation alloc] initWithLatitude:oldCoordinate.latitude longitude:oldCoordinate.longitude];
latitude =coordinate.latitude;
longitude =coordinate.longitude;
CLLocationDistance distDifference = [newlocation distanceFromLocation:oldlocation];
int distance = distDifference;
if (distance>0.5) {
if (isInternetReachable==TRUE) {
[self callWebserviceSetlocationInBackground:newLocation];
}
}
[newlocation release];
[oldlocation release];
}
Also you need to set "Required background modes" Flag to "App registers for location updates" to use core location updated in background.
http://i.stack.imgur.com/WtcCm.png
This may help you.
Just change calling web service code method by below method. I hope it will work:-
-(void) callWebserviceSetlocationInBackground:(CLLocation *)location
{
// 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.
UIApplication *app = [UIApplication sharedApplication];
UIBackgroundTaskIdentifier bgTask = [app beginBackgroundTaskWithExpirationHandler:^{ [app endBackgroundTask:bgTask];}];
NSLog(#"call web service in background lati = %f and long =%f",location.coordinate.latitude,location.coordinate.longitude);
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}
Thanks.
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
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
I'm working on an iphone app that occasionally fires a task in the background to rearrange some data and upload it to a server. I've used a lot of the principles from Grand Central Dispatch (GCD) with CoreData to get things running, since I'm editing objects that persist in Core Data, but the code only occasionally finishes running despite the application saying it has almost the full 600 seconds of execution time remaining.
The code i'm using:
__block UIBackgroundTaskIdentifier bgTask;
UIApplication *application = [UIApplication sharedApplication]; //Get the shared application instance
NSLog(#"BackgroundTimeRemaining before block: %f", application.backgroundTimeRemaining);
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.
NSLog(#"BackgroundTimeRemaining after block: %f", application.backgroundTimeRemaining);
NSLog(#"Fixing item in the background");
//Create secondary managed object context for new thread
NSManagedObjectContext *backgroundContext = [[NSManagedObjectContext alloc] init];
[backgroundContext setPersistentStoreCoordinator:[self.managedObjectContext persistentStoreCoordinator]];
/* Save the background context and handle the save notification */
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(backgroundContextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:backgroundContext];
//creating runloop to kill location manager when done
NSDate *stopDate = [[NSDate date] dateByAddingTimeInterval:60];
[[NSRunLoop currentRunLoop] runUntilDate:stopDate];
NSLog(#"Stop time = %#", stopDate);
MasterViewController *masterViewContoller = [[MasterViewController alloc] init];
masterViewContoller.managedObjectContext = backgroundContext;
[[masterViewContoller locationManager] startUpdatingLocation];
NSLog(#"Successfully fired up masterViewController class");
[masterViewContoller adjustDataInBackground:FALSE];
NSLog(#"Fixed Object!");
//save background context
[backgroundContext save:NULL];
//unregister self for notifications
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSManagedObjectContextDidSaveNotification
object:backgroundContext];
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
The issue is that "adjustDataInBackground:FALSE" is a pretty long method that calls additional supporting methods (including creation and saving of core data objects), and when the background task doesn't allow all of those methods to finish it corrupts my data.
Is there a better way of handling this kind of an operation? Do i need to put all my raw code into the background task block directly?
So it turns out I had two weird things going on that were tripping up the background task:
Asynchronous URL connections (when their initiating method finished, iOS thought the background task was done even if the response hadn't yet been received)
A location manager specific to the background task (apparently a major no-no...apple's got some documentation on this but the console would spit out an error about it sometimes)
Here's the code I'm now using (it works so far):
__block UIBackgroundTaskIdentifier bgTask;
UIApplication *application = [UIApplication sharedApplication]; //Get the shared application instance
NSLog(#"BackgroundTimeRemaining before block: %f", application.backgroundTimeRemaining);
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.
NSLog(#"BackgroundTimeRemaining after block: %f", application.backgroundTimeRemaining);
//Create secondary managed object context for new thread
NSManagedObjectContext *backgroundContext = [[NSManagedObjectContext alloc] init];
[backgroundContext setPersistentStoreCoordinator:[self.managedObjectContext persistentStoreCoordinator]];
/* Save the background context and handle the save notification */
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(backgroundContextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:backgroundContext];
//Set a grace period during which background updates can't stack up...
//This number should be more than the longest combo of timeout values in adjustDataInBackground
NSDate *stopDate = [[NSDate date] dateByAddingTimeInterval:90];
__lastBackgroundSnapshot = stopDate;
NSLog(#"Stop time = %#", stopDate);
MasterViewController *masterViewContoller = [[MasterViewController alloc] init];
masterViewContoller.managedObjectContext = backgroundContext;
NSLog(#"Successfully fired up masterViewController class");
[masterViewContoller adjustDataInBackground];
NSLog(#"adjustDataInBackground!");
//just in case
[[self locationManager] stopUpdatingLocation];
//save background context
[backgroundContext save:NULL];
NSLog(#"Uploading in background");
//send results to server
postToServer *uploadService = [[postToServer alloc] init];
uploadService.managedObjectContext = backgroundContext;
[uploadService uploadToServer];
//save background context after objects are marked as uploaded
[backgroundContext save:NULL];
//unregister self for notifications
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSManagedObjectContextDidSaveNotification
object:backgroundContext];
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
In addition, I added the following runloop to my asynchronous URLConnection objects so they stayed alive long enough to finish their business. While it's not the most graceful way of handling it, it works as long as you can handle the failure gracefully if the runloop ends without the server exchange finishing.
A runloop (adjusted for different timeouts depending on the task):
//marks the attempt as beginning
self.doneUpload = [NSNumber numberWithBool:FALSE];
[[uploadAttempt alloc] fireTheUploadMethod];
//if uploading in the background, initiate a runloop to keep this object alive until it times out or finishes
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground)
{
//Timeout length to wait in seconds to allow for async background execution
NSDate *stopDate = [[NSDate date] dateByAddingTimeInterval:120];
do {
NSLog(#"Waiting for upload to return, time left before timeout: %f", [stopDate timeIntervalSinceNow]);
[[NSRunLoop currentRunLoop] runUntilDate:stopDate];
} while ([stopDate timeIntervalSinceNow] > 0 && self.doneUpload == [NSNumber numberWithBool:FALSE]);
}
Hope this helps anyone who runs into this in the future!
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.
Make sure you are not testing this in simulator.
You don't get location updates on the iphone simulator.
In order to run location services in the background, you need to modify your plist such that the UIBackgroundModes key contains location.
the call should be
[locationManager startMonitoringSignificantLocationChanges];