I am working on walking track type app but facing problem that how location would upadte when app is in background . I am drawing a path on map as location update and a timer also .
Then please suggest me how i handle it.
here is my code
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
if(!newLocation) return;
if ((oldLocation.coordinate.latitude != newLocation.coordinate.latitude) &&
(oldLocation.coordinate.longitude != newLocation.coordinate.longitude))
{
// to draw path as new location find
jogPoint = [[JogPoint alloc] initWithCenterCoordinate:newLocation.coordinate];
[jogPoints addObject:jogPoint];
if([jogPoints count] >= 5) {
[self.mapView addOverlay:jogPoint];
CLLocation *loc1 = [[CLLocation alloc] initWithLatitude:oldLocation.coordinate.latitude longitude:oldLocation.coordinate.longitude];
CLLocation *loc2 = [[CLLocation alloc] initWithLatitude:newLocation.coordinate.latitude longitude:newLocation.coordinate.longitude];
// returns distance in meters
distnc+=[loc1 distanceFromLocation:loc2] ;
distanceLabel.text=[NSString stringWithFormat:#"%lf",distnc];
// jogInfo.distance += ([loc1 distanceFromLocation:loc2]) * 0.000621371192;
// jogInfo.eclapsedTime = (CFAbsoluteTimeGetCurrent() - startTime) * 1000 * 60;
}
}
}
If your Application is in background mode also location is tracking/update as like if your app. active so don't worry about it :)
Just Make sure your create CLLocationManager such like
self.currentLocation = [[CLLocationManager alloc]init];
self.currentLocation.desiredAccuracy = kCLLocationAccuracyHundredMeters;
self.currentLocation.delegate = self;
[self.currentLocation startUpdatingLocation];
EDITED :
If here set distanceFilter then your method call at specific meter which value set in distanceFilter otherwise it update whenever your device is move
And This is very important to set/add Require background mode in YourProjectName-Info.plist file
Value is App Registers for location update
Such like
You can use the significant-change location service to receive location events.
This service offers a significant power savings and provides accuracy
that is good enough for most apps. It uses the device’s cellular radio
to determine the user’s location and report changes in that location,
allowing the system to manage power usage much more aggressively than
it could otherwise. This service is also capable of waking up an app
that is currently suspended or not running in order to deliver new
location data.
To use the significant-change location service:
Create an instance of the CLLocationManager class, assign a delegate
to it, and call the startMonitoringSignificantLocationChanges method
. As location data becomes available, the
location manager notifies its assigned delegate object. If a location
update has already been delivered, you can also get the most recent
location data directly from the CLLocationManager object without
waiting for a new event to be delivered.
Related
I have a location app that needs to get accurate location periodically. Currently I am getting constantly getting location in didUpdateLocation but I only ever log the location every 5 seconds. I am interested in a solution that gets accurate location periodically or on signification change. I would like either or both of these scenarios:
(by very accurate, I need 10m of desired accuracy)
Get a very accurate location every 5 seconds
Notify/callback if user moves a threshold ( eg moves 5 - 10 meters)
The app needs to work when backgrounded as well and location must still be logged if user switches to another app.
I was considering turning on/off location every 5 seconds but was not sure if that is the best practice. I also know there is also allowDeferredLocationUpdatesUntilTraveled but I believe that only applied to backgrounded mode. I would appreciate a solution that saves battery when the app is in use and in background mode. Please share your solutions and best practices for my use case.
I did write an app using Location services, app must send location every 10s. And it worked very well.
Just use the "allowDeferredLocationUpdatesUntilTraveled:timeout" method, following Apple's doc.
Steps are as follows:
Required: Register background mode for update Location.
Create LocationManger and startUpdatingLocation, with accuracy and filteredDistance as whatever you want:
-(void) initLocationManager
{
// Create the manager object
self.locationManager = [[[CLLocationManager alloc] init] autorelease];
_locationManager.delegate = self;
// This is the most important property to set for the manager. It ultimately determines how the manager will
// attempt to acquire location and thus, the amount of power that will be consumed.
_locationManager.desiredAccuracy = 45;
_locationManager.distanceFilter = 100;
// Once configured, the location manager must be "started".
[_locationManager startUpdatingLocation];
}
To keep app run forever using "allowDeferredLocationUpdatesUntilTraveled:timeout" method in background, you must restart updatingLocation with new parameter when app moves to background, like this:
- (void)applicationWillResignActive:(UIApplication *)application {
_isBackgroundMode = YES;
[_locationManager stopUpdatingLocation];
[_locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
[_locationManager setDistanceFilter:kCLDistanceFilterNone];
_locationManager.pausesLocationUpdatesAutomatically = NO;
_locationManager.activityType = CLActivityTypeAutomotiveNavigation;
[_locationManager startUpdatingLocation];
}
App gets updatedLocations as normal with "locationManager:didUpdateLocations:" callback:
-(void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
// store data
CLLocation *newLocation = [locations lastObject];
self.userLocation = newLocation;
//tell the centralManager that you want to deferred this updatedLocation
if (_isBackgroundMode && !_deferringUpdates)
{
_deferringUpdates = YES;
[self.locationManager allowDeferredLocationUpdatesUntilTraveled:CLLocationDistanceMax timeout:10];
}
}
But you should handle the data in then "locationManager:didFinishDeferredUpdatesWithError:" callback for your purpose
- (void) locationManager:(CLLocationManager *)manager didFinishDeferredUpdatesWithError:(NSError *)error {
_deferringUpdates = NO;
//do something
}
NOTE: I think we should reset parameters of LocationManager each time app switches between background/forgeround mode.
Hopefully this should help
I'm first using the CoreLocation framework. I have a table and by button click a new location should be added and the distance to all entries in the table should be shown and updated all the time. That is why I have a BOOL saveNewLocation which is set to Yes when the button is clicked. Because the updates need to happen still all the time in the background, but when the button is clicked only a new entry is added.
At the moment I have this in my viewDidLoad:
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
// Check for iOS 8. Without this guard the code will crash with "unknown selector" on iOS 7.
if ([self.locationManager respondsToSelector:#selector(requestWhenInUseAuthorization)]) {
[self.locationManager requestWhenInUseAuthorization];
}
[self.locationManager startUpdatingLocation];
And this is my delegate method:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
self.currentLocation = newLocation;
if(self.saveNewLocation){
[PointOfInterest addPointOfInterest:newLocation withAddress:#"" andNotes:#"" inManagedObjectContext:self.cdh.context];
self.saveNewLocation = NO;
}
[self updateAllDistances];
}
And this my button:
- (IBAction)addLocationClicked:(id)sender {
self.saveNewLocation = YES;
}
But the problem at the moment is that when you click this button, there is sometimes a big lag and nothing happens. Sometimes immediately a new location is added. How can I avoid this lag and instantly add a new location by click?
The time interval between update calls to the location manager delegate is variable, so the behavior you're experiencing is expected.
CLLocationManager has a property called location which returns the last known location of the user (or nil if you've never used the Location Manager in the app).
Instead of waiting for the LocationManager to update, grab the last known location of the user instead:
- (IBAction)addLocationClicked:(id)sender {
CLLocation *location = self.locationManager.location;
if (location && [NSDate timeIntervalSinceReferenceDate] - location.timeStamp.timeIntervalSinceReferenceDate < 60 * 10){
//Do something with the location if the location manager returns a location within the last 10 minutes
} else {
self.saveNewLocation = YES;
}
}
If the app has never asked for it's location, you may get nil, in which case you'll have to wait for the locationManager to update. But otherwise, you can just grab the last known location. You can also check whether the location was update recently by checking the timeStamp on the location object.
You also may want to set a state flag indicating that the app should wait for a location update when the location manager is first used. When you first start up the LocationManager, you can't really know how up-to-date that location is. But once the manager begins updating the delegate, you can be reasonably certain the location manager holds a fairly up-to-date location.
Ive been struggling with a way to retrieve information periodically from a BT device. My bluetooth device is located in a vehicle typically, so my question is if its possible to use say... (if user traveling > 10km/h) to run a task. Or on major location change.
Is is possible to get a really course location that I would be able to use to get a general idea of wether the user is moving? I only need it to trigger once every couple days(while user is driving). The user never interacts with my app after initial setup.
Thanks.
Implementation of cmyr's suggestion:
CLLocationManager *locationManager;
int badge_count = 0;
- (void)startSignificantChangeUpdates
{
// Create the location manager if this object does not
// already have one.
if (nil == locationManager)
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.pausesLocationUpdatesAutomatically = YES;
locationManager.activityType = CLActivityTypeAutomotiveNavigation;
[locationManager requestAlwaysAuthorization];
locationManager.desiredAccuracy = kCLLocationAccuracyKilometer;
locationManager.distanceFilter = 500; // meters
[locationManager allowDeferredLocationUpdatesUntilTraveled:501 timeout:-1];
[locationManager startMonitoringSignificantLocationChanges];
[locationManager startUpdatingLocation];
}
// Delegate method from the CLLocationManagerDelegate protocol.
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations {
badge_count++;
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:badge_count];
NSLog(#"Location Event WOOT!");
}
Unfortunately I cannot get the event to trigger. I have added Location updated to the apps plist.
The above code is contained inside my app delegate.m file
Core Location has a set of APIs for specifically this use-case, which Apple refers to as Significant Location Change Monitoring.
From the documentation:
The significant-change location service delivers updates only when there has been a significant change in the device’s location, such as 500 meters or more.
This API only updates your location when and if you've traveled the specified distance. It does not provide constant updates. If you need constant updates, you will have to use the standard location service.
{
...
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
}
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
NSLog(#"myLocation1: %#",[locations lastObject]);
myLocation = [locations lastObject];
NSLog(#"myLocation2: %#",[locations lastObject]);
[manager stopUpdatingLocation];
[self doSomethingWithLocation];
}
Currently I'm in the location 40.000,40.000.
I'm closing my app and change location to 10.000,10.000
When entering the app again and running [locationManager startUpdatingLocation]; my log will show:
myLocation1: <+40.00000000,+40.00000000>
myLocation2: <+40.00000000,+40.00000000>
If I'll trigger [locationManager startUpdatingLocation]; again my log will show:
myLocation1: <+10.00000000,+10.00000000>
myLocation2: <+10.00000000,+10.00000000>
How can I call didUpdateLocations once and still get the current location?
Should I use another delegate?
I guess I could place stopUpdatingLocation inside doSomethingWithLocation and run doSomethingWithLocation after some sort of delay in order for the right location to be updated but I'm sure that's not the way it's meant to be.
Thanks
Leave the location manager running for a while (e.g. 30 seconds), setting a timer to tell it to stop. The location manager updates are like pancakes, the first one you get isn't always the best.
The first update you are seeing is likely a "stale" location, which was determined many minutes ago when location services were last powered up. Or it may be a very inaccurate location determined using cell-tower positioning, for example. If you just need to get the device's current location, using Core Location directly requires a good deal of code because you must handle these cases. (The CLLocationManager API appears to be built for apps that need continuous location updates, like turn-by-turn GPS navigation apps.)
Instead of using CLLocationManager directly, I suggest you take a look at using an open source component such as INTULocationManager which will handle all of this work for you and make it trivially simple to request one or more discrete requests for the device's current location.
In this case you should check timestamp of location. User does not move on such distances so quickly.
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
CLLocation *location = [locations lastObject];
if(fabs([location.timestamp timeIntervalSinceNow]) < 5)//seconds
{
myLocation = location;
manager.delegate = nil;
[manager stopUpdatingLocation];
[self doSomethingWithLocation];
}
}
I'm looking for an open source app or library to track user location in the background. Now I'm trying to do it with CLLocation and background tasks, but accuracy is not enough for my case. Could you explain, how apps, like "moves", "runkeeper", "endmondo", creates my route? Should I use Accelerometer or/and compass to create a route between CLLocation background points?
Some code:
//location manager init
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.distanceFilter = kCLDistanceFilterNone;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.delegate = self;
#pragma mark - CLLocationManager Delegate
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
if ([self isInBackground]) {
if (self.locationUpdatedInBackground) {
bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler: ^{
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
}];
self.locationUpdatedInBackground(newLocation);
[self endBackgroundTask];
}
} else {
if (self.locationUpdatedInForeground) {
self.locationUpdatedInForeground(newLocation);
}
}
}
UPD:
Justed tested my app with next properties
self.locationManager.distanceFilter = kCLDistanceFilterNone;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.activityType = CLActivityTypeFitness;
self.locationManager.pausesLocationUpdatesAutomatically=NO;
In this case I have about 10 fired events during 1,5 hour trip
Use
kCLLocationAccuracyBestForNavigation
check this.
You need to add in your "Info.plist" file the key UIBackgroundModes (array) with the value "location" (app registers for location updates).
You can check all background modes here.
So your app uses location services. Then please read the Location Awareness Programming Guide.
You need to make some changes to your Info.plist:
If your app relies on location services to function properly, add location-services to UIRequiredDeviceCapabilities
if your app requires GPS hardware, add gps to UIRequiredDeviceCapabilities
if you need to run your app longer then 10 minutes in the background, add location to UIBackgroundModes. Then your location manager will deliver locations beyond the 10-minute-limit.
you should also set NSLocationUsageDescription (can also be localized)
Getting Location Events in the Background
If your app needs location updates delivered whether the app is in the foreground or background, there are multiple options for doing so. The preferred option is to use the significant location change service to wake your app at appropriate times to handle new events. However, if your app needs to use the standard location service, you can declare your app as needing background location services.
An app should request background location services only if the absence of those services would impair its ability to operate. In addition, any app that requests background location services should use those services to provide a tangible benefit to the user. For example, a turn-by-turn navigation app would be a likely candidate for background location services because of its need to track the user’s position and report when it is time to make the next turn.
Your problem is the background handler. Remove it and enable gps background mode in plist file. then you should get full power gps all the time.
Set property pausesLocationUpdatesAutomatically=NO
This is new in ios6.
From CLLocationManager:
Allowing the location manager to pause updates can improve battery
life on the target device without sacrificing location data. When this
property is set to YES, the location manager pauses updates (and
powers down the appropriate hardware) at times when the location data
is unlikely to change. For example, if the user stops for food while
using a navigation app, the location manager might pause updates for a
period of time. You can help the determination of when to pause
location updates by assigning a value to the activityType property.
The default value of this property is YES.
For analysis add these methods to your LocationManager delegate:
- (void)locationManagerDidPauseLocationUpdates:(CLLocationManager *)manager {
NSLog(#"locMan: locationManagerDidPauseLocationUpdates");
}
- (void)locationManagerDidResumeLocationUpdates:(CLLocationManager *)manager {
NSLog(#"locMan: locationManagerDidResumeLocationUpdates");
}
You can set up monitoring location stuff in you VC as below
in viewDidLoad method do as below
CLLocationManager locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;(Accuracy according to your need)
[locationManager startUpdatingLocation];
than you have to overrite below two optional delegate methods of CLLocationManagerDelegate protocol
for iOS6+
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{}
and for iOS 2 to 6
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
in these methods you will get updated location. use it as you want.
every time location updated these method get calls.
you don't ask for code. You ask for: "I'm looking for an open source app or library"
It may help you to visit this website.
hope it helps you,
Also a tutorial.
Here was my solution to this,
Declare the instance variable:
CLLocationManager *locationManager;
Be sure to include the delegate
<CLLocationManagerDelegate>
In viewDidLoad:
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone; // whenever we move, location is updated
locationManager.desiredAccuracy = kCLLocationAccuracyBest; // get best current locaton coords
locationManager.headingFilter = 1;
[locationManager startUpdatingLocation];
[locationManager startUpdatingHeading];
Implement the delegate method:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
int degrees = newLocation.coordinate.latitude;
double decimal = fabs(newLocation.coordinate.latitude - degrees);
int minutes = decimal * 60;
double seconds = decimal * 3600 - minutes * 60;
NSString *lat = [NSString stringWithFormat:#"%d° %d' %1.4f\"",
degrees, minutes, seconds];
NSLog(#" Current Latitude : %#",lat);
latitudeLocation.text = lat;
degrees = newLocation.coordinate.longitude;
decimal = fabs(newLocation.coordinate.longitude - degrees);
minutes = decimal * 60;
seconds = decimal * 3600 - minutes * 60;
NSString *longt = [NSString stringWithFormat:#"%d° %d' %1.4f\"",
degrees, minutes, seconds];
NSLog(#" Current Longitude : %#",longt);
longitudeLocation.text = longt;
}
Disclaimer: I work for Cintric
We also wanted to be able to accurately track a users location in background (even after the app had been killed). We spent a long time solving the problem, especially focusing on battery drain.
We posted a great blog post on how we solved it. And also are providing a drag and drop SDK solution. It's not open source but you can integrate it for free:
You can download the .framework file, drag it into your project and initialize with one line of code:
[CintricFind initWithApiKey:#"YOUR_API_KEY_HERE" andSecret:#"YOUR_SECRET_HERE"];
You can get an API key for free. There are docs explaining everything here.