I need to create an app that retrieves iOS radio informations (rssi, bearer, ...) in background mode ; the app will be available on app store and thereby is under App Store guidelines.
The app should rationally keep the phone's battery.
I currently use BackgroundFetch and GPS together, it works pretty good but when iOS has not enough memory the app is killed by OS.
I also looked Google app (Google Now) that uses GPS and keep iPhone's battery life, but i don't know how they do that.
Thanks
Make sure "Background Fetch" and "Location Updates" are enabled in your Plist.
When your app is in the background, user startMonitoringSignificantLocationChanges instead of startUpdatingLocation. Here's some info from the documentation.
"[startUpdatingLocation] typically require the location-tracking hardware to be enabled for longer periods of time, which can result in higher power usage."
"[With startMonitoringSignificantLocationChanges] Apps can expect a notification as soon as the device moves 500 meters or more from its previous notification. It should not expect notifications more frequently than once every five minutes."
I'd go through the documentation here so you can grab a better understanding (https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/CLLocationManager/CLLocationManager.html#//apple_ref/occ/instm/CLLocationManager/startMonitoringSignificantLocationChanges)
As you can probably see, using startMonitoringSignificantLocationChanges significantly increases battery life. Here's how I implemented it in my AppDelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
...
[self.locationManager startMonitoringSignificantLocationChanges];
...
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
...
[self.locationManager stopUpdatingLocation];
[self.locationManager startMonitoringSignificantLocationChanges];
...
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
...
[self.locationManager stopMonitoringSignificantLocationChanges];
[self.locationManager startUpdatingLocation];
...
}
-(CLLocationManager *)locationManager{
if(!_locationManager){
_locationManager = [[CLLocationManager alloc]init];
_locationManager.delegate = self;
_locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
_locationManager.distanceFilter = 5;
}
return _locationManager;
}
This way I ensure that I only use the power-draining "startUpdatingLocation" method when the app is active and the power-saving "stopMonitoringSignificantLocationChanges" when inactive.
I made on test on your code above.
-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{
//NSLog(#"Location: %f, %f", newLocation.coordinates.longtitude, newLocation.coordinates.latitude);
longitude = newLocation.coordinate.longitude;
latitude = newLocation.coordinate.latitude;
gpsTimestamp = [newLocation.timestamp timeIntervalSince1970];
NSLog(#"Changed ! ");
if (!timer && ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground)) {
NSLog(#"Starting task");
timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(createBgIndicator) userInfo:nil repeats:YES];
}
}
In this case, the timer will start only while I'm moving and is executed 5 times (10s). I would like to get the timer running all the time.
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 have gone through so many links, even after that I haven't found a proper solution for getting latitude and longitude.
Periodic iOS background location updates
iOS long-running background timer with "location" background mode
I tried from some links and forums but it is working for only 3 mins, then app is not at all updating the user location.
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
//create new uiBackgroundTask
__block UIBackgroundTaskIdentifier bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
//and create new timer with async call:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[locationManager startUpdatingLocation];
NSTimer* t = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:#selector(startTrackingBg) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:t forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
});
}
-(void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
// store data
CLLocation *newLocation = [locations lastObject];
//tell the centralManager that you want to deferred this updatedLocation
if (_isBackgroundMode && !_deferringUpdates)
{
_deferringUpdates = YES;
[locationManager allowDeferredLocationUpdatesUntilTraveled:CLLocationDistanceMax timeout:10];
}
}
Ok.
After struggling for 3days, it is working for me for sending latitude and longitude when app is in background even after 3 mins.
I checked my app, continuously sending lat long for more than a hour in background.
It can help some one at least.
First Please add below two keys in your pList.
1.NSLocationAlwaysUsageDescription
2.NSLocationWhenInUseUsageDescription
Bothe are strings and you can give any value.
Then please turn on background fetch and check location updates under capabilities in project section.
Then import Corelocation framework and add this below code.
locationManager is a global variable.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
//create CLLocationManager variable
locationManager = [[CLLocationManager alloc] init];
//set delegate
locationManager.delegate = self;
app = [UIApplication sharedApplication];
// 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.
if ([locationManager respondsToSelector:#selector(setAllowsBackgroundLocationUpdates:)]) {
[locationManager setAllowsBackgroundLocationUpdates:YES];
}
locationManager.desiredAccuracy = 45;
locationManager.distanceFilter = 100;
// Once configured, the location manager must be "started".
[locationManager startUpdatingLocation];
}
- (void)applicationWillResignActive:(UIApplication *)application {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
[locationManager stopUpdatingLocation];
[locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
[locationManager setDistanceFilter:kCLDistanceFilterNone];
locationManager.pausesLocationUpdatesAutomatically = NO;
locationManager.activityType = CLActivityTypeAutomotiveNavigation;
[locationManager startUpdatingLocation];
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
[locationManager stopUpdatingLocation];
__block UIBackgroundTaskIdentifier bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
bgTask = UIBackgroundTaskInvalid;
}];
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:10.0
target:self
selector:#selector(startTrackingBg)
userInfo:nil
repeats:YES];
}
-(void)startTrackingBg {
[locationManager startUpdatingLocation];
NSLog(#"App is running in background");
}
//starts automatically with locationManager
-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{
latitude=newLocation.coordinate.latitude;
longitude=newLocation.coordinate.longitude;
NSLog(#"Location: %f, %f",newLocation.coordinate.longitude, newLocation.coordinate.latitude);
}
You need to refer this apple documentation handling location events in the background
You need to enable location updates in background modes in capabilities of your Xcode project.
The Standard location service wont work in background mode so you have to use either Significant-change location service or Visits service .
Use this code to
locationManager.delegate = self
locationManager.startMonitoringSignificantLocationChanges()
enable Significant-change location service.
Building on Santos answer which has most of the important steps to make background positioning work, I have clarified and corrected some details.
Project settings
You should add these keys to your Xcode Target Info property list. Make sure to add a valid description to each of them, or your app might fail approval.
NSLocationAlwaysUsageDescription
NSLocationWhenInUseUsageDescription
NSLocationAlwaysAndWhenInUseUsageDescription
Next, in Target Capabilities, Turn on Background Modes and check Location updates and Background fetch. Location updates will enable locations in the background and background fetch will allow you to use the network in the background.
Start monitoring
Start by creating an instance of CLLocationManager, configure it and turn on background updates, then start it. Although the code below uses the most common function startUpdatingLocation to get locations, there are several other services to use. It is important to choose the most suitable service as this impacts greatly on battery usage and if the app will be re-launched or not by iOS. See below for more info on this.
// Declare as properties in your class
#property (strong, nonatomic) CLLocationManager *locationManager;
#property (strong, nonatomic) CLLocation *lastLocation;
// Call to start
- (void)initializeAndStartLocationService {
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
// Must be set for background operation
self.locationManager.allowsBackgroundLocationUpdates = YES;
// Optional configuration
self.locationManager.distanceFilter = kCLDistanceFilterNone;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.pausesLocationUpdatesAutomatically = YES;
self.locationManager.showsBackgroundLocationIndicator = YES;
// Authorize - the lazy way
[self.locationManager requestAlwaysAuthorization];
// Start the standard location service, there are others
[self.locationManager startUpdatingLocation];
}
// Delegate method that will receive location updates
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
// Keep the last received location
self.lastLocation = [locations lastObject];
NSLog(#"New position %f, %f", self.lastLocation.coordinate.latitude, self.lastLocation.coordinate.longitude);
// Do something with the location or retrieve the location later
// :
}
Stop monitoring
Don't forget to stop monitoring to conserve battery. You can start and stop several times on a single instance of CLLocationManager.
- (void)dealloc {
[self.locationManager stopUpdatingLocation];
}
Automatic app re-launch considerations
When your app is running in the background it can (and actually frequently will after some time) be terminated by iOS. Depending on the type of location updates you are using, iOS will or will not re-launch your app automatically for you. In cases where iOS do not re-launch, the user must start your app again to continue background processing.
Read more on this page.
Read more
CLLocationManager - Core Location | Apple Documentation
Handling Location Events in the Background | Apple Documentation
I'm trying to implement the deferred location updates to have a better battery consumption.
I'm starting my location manager like this :
- (void)initCoreLocation
{
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.distanceFilter = kCLDistanceFilterNone;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.pausesLocationUpdatesAutomatically = YES;
self.locationManager.activityType = CLActivityTypeAutomotiveNavigation;
//Très important pour iOS9 !
if ([self.locationManager respondsToSelector:#selector(allowsBackgroundLocationUpdates)]) {
self.locationManager.allowsBackgroundLocationUpdates=YES;
}
if ([self.locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]) {
[self.locationManager requestAlwaysAuthorization];
}
[self.locationManager startUpdatingLocation];
[self.locationManager startMonitoringSignificantLocationChanges];
}
And starting deferred update like this way:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
if (!self.deferringUpdates) {
[self.locationManager allowDeferredLocationUpdatesUntilTraveled:CLLocationDistanceMax timeout:30];
self.deferringUpdates = YES;
}
}
-(void)locationManager:(CLLocationManager *)manager didFinishDeferredUpdatesWithError:(NSError *)error { // Stop deferring updates
if(error) {
NSLog(#"error");
}
NSLog(#"didFinishDeferredUpdates");
self.deferringUpdates = NO;
}
I have didFinishDeferredUpdates log every 30 seconds, however didUpdateLocations keeps calling every second, removing any try to optimise the battery consumption. Is it supposing to the location manager to call the didUpdateLocations every 30 seconds ?
Maybe you are not having the right approach as allowDeferredLocationUpdatesUntilTraveled() tells the GPS hardware to store new locations internally until the specified distance or timeout conditions are met.
From the iOS Developer Library:
If your app is in the foreground, the location manager does not defer the deliver of events but does monitor for the specified criteria. If your app moves to the background before the criteria are met, the location manager may begin deferring the delivery of events. Link
Are you debugging?
As stated in the answer here: ios deferred location updates fail to defer
Deferred updates are delivered only when the system enters a low power state. Deferred updates do not occur during debugging because Xcode prevents your app from sleeping and thus prevents the system from entering that low power state.
I believe you only need one of them at one time, change the call in applicationDidEnterBackground and applicationWillEnterForeground
- (**void)applicationDidEnterBackground:(UIApplication *)application {
// Need to stop regular updates first
[self.locationManager stopUpdatingLocation];
// Only monitor significant changes
[self.locationManager startMonitoringSignificantLocationChanges];
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
[self.locationManager stopMonitoringSignificantLocationChanges];
[self.locationManager startUpdatingLocation];
}
I am developing the IOS app with location server. In the app I am checking the user location periodically, and send the location details to the server.
Following are the steps:
1: App registers for location updates( in plist).
2: added the code like:
-(void)startUpdating{
locationManager = [[CLLocationManager alloc]init];
locationManager.delegate = self;
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];
locationManager.pausesLocationUpdatesAutomatically = YES;
[locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
locationManager.distanceFilter = kCLDistanceFilterNone;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
dispatch_async(dispatch_get_main_queue(), ^{
//call web service location
[self UpdateAppmyLocation:#"1"];
});
[locationManager allowDeferredLocationUpdatesUntilTraveled:200 timeout:(NSTimeInterval)90];
}
- (void)applicationWillResignActive:(UIApplication *)application
{
[locationManager startUpdatingLocation];
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
[locationManager stopUpdatingLocation];
}
3: startUpdating method called in didFinishLaunchingWithOptions.
Everything is working, but the didUpdateLocations:locations method called every minute. I am trying to add delay for some time so it will not lead to battery issue.
Note: Testing the app on iPhone 5 with iOS 7.1
Please let me know the solution.
https://github.com/voyage11/Location
Use this code.you can set time after how much time you want to call it.
I have tried many codes, but i found this as most accurate and list battery issue.This is also awesome for background location service.
I have an app which is set to monitor entry into regions. On entry, the app will alert the user with a local notification.
This works fine when the app is on, or in the background. However if the app is terminated, the region monitoring never raises the local notification.
I have set my "background modes" key in the info.plist.
Could this be because my CLLocation code is not in the AppDelegate (instead it is in a singleton)?
Could this be because it's not possible to run code to raise the location notification from a terminated state?
Here's my code when the region is entered:
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
UIApplication* app = [UIApplication sharedApplication];
UILocalNotification* notifyAlarm = [[UILocalNotification alloc] init];
notifyAlarm.fireDate = [NSDate dateWithTimeIntervalSinceNow:5];;
notifyAlarm.timeZone = [NSTimeZone defaultTimeZone];
notifyAlarm.repeatInterval =NSDayCalendarUnit;
notifyAlarm.alertBody = [Installation currentInstallation].reminderText;
[app scheduleLocalNotification:notifyAlarm];
}
There are 2 things happen:
a) If the application is suspended when an update occurs, the system wakes it up in the background to handle the update.
b) If the application starts this service and is then terminated, the system relaunches the application automatically when a new location becomes available.
What we can now do is turn on significant location updates when the user hits the home key and we can let the system wake us up when needed.
-(void) applicationDidEnterBackground:(UIApplication *) application
{
// You will also want to check if the user would like background location
// tracking and check that you are on a device that supports this feature.
// Also you will want to see if location services are enabled at all.
// All this code is stripped back to the bare bones to show the structure
// of what is needed.
[locationManager startMonitoringSignificantLocationChanges];
}
Then to perhaps switch to higher accuracy when the application is started up, use;
-(void) applicationDidBecomeActive:(UIApplication *) application
{
[locationManager stopMonitoringSignificantLocationChanges];
[locationManager startUpdatingLocation];
}
Next you’ll likely want to change your location manager delegate to handle background location updates.
-(void) locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
BOOL isInBackground = NO;
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground)
{
isInBackground = YES;
}
// Handle location updates as normal.
if (isInBackground)
{
// Do, if you have to send location to server, or what you need
}
else
{
// ...
}
}
Please Note: Location monitoring in background will take effect on battery usage.
This code is taken from http://www.mindsizzlers.com/2011/07/ios-background-location/