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.
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
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 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.
I have three points, but really only the first one is the most important
In order to preserve battery life, I'm trying to have the locationManager turn on just long enough to get the user's location and then shut off. What is the best means to do so?
I don't always need to know the user's location, but having a relatively accurate location when the user hits search is important (within perhaps a couple dozen city blocks, 10,000 meters, maybe even less accurate).
I'm sending a request to a server and then getting the results, and after THAT delay, I need a more accurate version of the user location (within 100 meters is fine).
I don't know how much of this is too nitpicky, but if the last two points are possible/efficient battery-wise, then please let me know how to do it!
You can easily stop tracking location when your CLLocationManager's delegate receives a location update meeting your desired accuracy.
To start updating location, do something like:
CLLocationManager *locationManager = [CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
[locationManager startUpdatingLocation];
Then implement the appropriate CLLocationManagerDelegate method:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *foundLocation = [locations lastObject];
if (foundLocation && foundLocation.horizontalAccuracy < kYourDesiredAccuracyInMetres)
{
[manager stopUpdatingLocation];
//Do whatever else with the location you've established
}
}
You should be able to tweak this meet your requirements, by checking the accuracy of the returned locations returned and either stopping the updates or letting them continue (if you need better accuracy).
It's also a good idea to set a timer when you start updating location, and stop updates if you haven't found a location within a set amount of time. You should also implement the locationManager:didFailWithError: delegate method to check whether you can access location services at all.
LocationManager could be stopped after location information of user has been retrieved .
CLLocationManager* locationManager = [ [ CLLocationManager alloc] init];
locationManager.delegate = self; //we must implement the protocol
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.headingFilter = kCLHeadingFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
//To turn on gps (if it isn't on already)
[locationManager startUpdatingLocation];
//To turn gps off (if no other apps are listening)
[locationManager stopUpdatingLocation];