Periodic location in background in iOS9 - ios

I'm trying to track location using locationManager in the background every "time interval" when the app is in the background.
Its working great on iOS 8 iPhone 6 - but i keep getting terminated after 2.5 - 3 minutes, by the iOS in iOS 9 iPhone 6s.
My main question is - what can be the difference between iPhone 6 iOS 8 and 6s iOS 9? better watchdog timer for background tasks? if so how can i workaround that?
I sow solutions that includes decrease of accuracy to save battery life instead of time intervals, I don't want that because the user is seeing the location monitoring indication all the time(full arrow).
some of my code -
-(void)initialize
{
self.locationManager = [[CLLocationManager alloc]init];
self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
self.locationManager.distanceFilter = 5;
self.locationManager.delegate = self;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(addMonitorsBeforeWillTerminate)
name:UIApplicationWillTerminateNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidEnterBackgroundNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
[[UIApplication sharedApplication]beginBackgroundTaskWithExpirationHandler:nil];
}];
[self.locationManager startUpdatingLocation];
}
thanks

#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"9.0")){
[self.locationManager setAllowsBackgroundLocationUpdates:YES];
}
More depth and breadth information could be found in the following link.

Two options I recommend.
When in background switch to kCLLocationAccuracyThreeKilometers. That will switch off GPS and won't drain the battery because it will use cell information only. Request permission for background location services and create an NSTimer callback (this will keep your app running in the BG). Inside the callback switch to the desired accuracy, make the update of the location then switch back to low accuracy again.
Sign up for significant location changes. With that however your app won't be notified every X minutes.

For however struggling with that in the future, the answer came from "technerd" by comment so i turned it in to an answer -
https://github.com/voyage11/Location

Related

IOS UIDeviceBatteryLevelDidChangeNotification not firing in background mode

I'm writing an IOS application which will record ( over time ) the current battery level of the iPhone device.
I have working code executing within the foreground using UIDeviceBatteryLevelDidChangeNotification, this fires a notification ( which is handled successfully ) when the app in in foreground mode.
Code is as follows:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[[UIDevice currentDevice] setBatteryMonitoringEnabled:YES];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(batteryChanged:) name:UIDeviceBatteryLevelDidChangeNotification object:nil];
return YES;
}
- (void)batteryChanged:(NSNotification *)notification
{
NSLog(#"Battery Changed From Observer");
[self UpdateBatteryStatus];
}
-(void)UpdateBatteryStatus
{
UIDevice *myDevice = [UIDevice currentDevice];
double batLeft = (float)[myDevice batteryLevel] * 100;
NSLog(#"Battery level: %f", batLeft);
// Do some stuff related to battery status
}
This executes ( output via log proves it ) every 1% increment ( or decrement ) on the battery status on the phone, which works exactly as expected.
When the app is moved to the background however, the events stop firing. Upon moving the app back to the foreground, the events fire ( it's almost like they are queued up until it reaches the foreground ).
I have searched stack overflow, and most answers are all the same, basically this is a background-mode permissions problem. So I ticked all the boxes:
I added everything just to make sure it wasn't a background persmission problem. Unfortunately, even after a clean, and rebuild ( also a shutdown of Xcode, and re-plugin of iPhone ) the events still do not fire.
Please bear in mind, I have no intention of releasing this to the app store, I'm aware that faking background-mode use cases is a quick and easy rejection. But I would like to get it working for personal usage.
Edit: ios target 11.3
Your app won't get NSNotificationCenter notifications while being in background/suspended. What you could try is to use fetch background mode to mimic remote downloads. Note however downsides of this approach - system itself will decide when to call the callback.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
return YES;
}
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
UIDevice *myDevice = [UIDevice currentDevice];
double batLeft = (float)[myDevice batteryLevel] * 100;
NSLog(#"Battery level: %f", batLeft);
completionHandler(UIBackgroundFetchResultNewData);
}
If you as stated do not plan to release your app in AppStore you can try another hacks with background modes like infinite playing a muted sound in background.

How to use proximity sensor programmatically with iOS development?

After some googling, I can understand that the "proximity sensor" which is used to on/off screen when the device is away/near from the user. I watched this video (watch from 30th sec) and surprised about this cool stuff. I want to implement it in my app.
But I come to know that there is no public API is available that can protect the screen lock when proximityMonitoringEnabled is YES. Then how can the above app did this?
For clear understanding, I'm copying some code.
Enable the proximity sensor:
[[UIDevice currentDevice] setProximityMonitoringEnabled:YES];
Setup an observer for sensor change:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(sensorStateMonitor:) name:#"UIDeviceProximityStateDidChangeNotification" object:nil];
Finally you can find the state of proximity sensor from this method:
- (void)sensorStateMonitor:(NSNotificationCenter *)notification
{
if ([[UIDevice currentDevice] proximityState] == YES)
{
NSLog(#"Device is close to user.");
}
else
{
NSLog(#"Device is not closer to user.");
}
}
Question:
I want to show some view when the "Device is close to user" state was called. And want to remove the view if "Device is not closer to user" state was called.
So I added a view and removed inside the sensorStateMonitor: method. But the view was visible only for some fraction of seconds and the screen goes off.
Can I prevent the screen from auto off?
Just confused!!
The screen lock can be enabled/disabled.
[UIApplication sharedApplication].idleTimerDisabled = YES;

how to fetch battery usage and level of my devices in ios

I want to get battery usage data from my iPhone. I used UIDevice currentDevice.batteryLevel code, but its returning -1.0000 in NSLog value.
Anyone please help me?
One more question, can I able to fetch other app battery usages in my app?
First, you must enable batteryStatus notification (in appDelegate.m for instance):
- (void)applicationDidBecomeActive:(UIApplication *)application {
...
[[UIDevice currentDevice] setBatteryMonitoringEnabled:YES];
// Now, the system will post UIDeviceBatteryStateDidChangeNotification notification when batteryStatus (or connection) will change
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(batteryStatusDidChange:) name:UIDeviceBatteryStateDidChangeNotification object:nil];
...
}
Then, you can check battery level calling:
[UIDevice currentDevice].batteryLevel;
that will return you a value between 0.0 (0% chareg) and 1.0 (100% charge).
Without calling first setBatteryMonitoringEnabled, battery state will return UIDeviceBatteryStateUnknown and the value of this property is –1.0. (from docs).

Rejected due to background location mode

my app update (1.1) was rejected because of this reason:
We found that your app uses a background mode but does not include functionality that requires that mode to run persistently.
But my app still uses the same functionality in version 1.0.
What i do:
On location update i check if the new location is inside a specific region (rectangle):
- (BOOL)locationInRegion: (CLLocation *) lastLocation {
if ((lastLocation.coordinate.latitude < self.overlayTopLeftCoordinate.latitude && lastLocation.coordinate.latitude > self.overlayBottomLeftCoordinate.latitude) &&
(lastLocation.coordinate.longitude < self.overlayTopRightCoordinate.longitude && lastLocation.coordinate.longitude > self.overlayTopLeftCoordinate.longitude)) {
return YES;
}
return NO;
}
In Foreground and in background mode if the user is in this region i draw a crumb path on a MKMapView. If not, i do nothing.
Require background modes -> app registers for location updates is in my .plist
What i am doing wrong?
I do not have this information in my description:
Continued use of GPS running in the background can dramatically
decrease battery life.
Can this be the (only) reason?
There are 2 possible reasons that your app is rejected.
It is either:-
a) Your app does not need location update when it is in the background.
b) Your app does not handle the background location updates properly
If it is the latter, you will need to handle the location update and also the background tasks when it is in the background. You will need something like this
- (id)init {
if (self==[super init]) {
//Get the share model and also initialize myLocationArray
self.shareModel = [LocationShareModel sharedModel];
self.shareModel.myLocationArray = [[NSMutableArray alloc]init];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
}
return self;
}
-(void)applicationEnterBackground{
CLLocationManager *locationManager = [LocationTracker sharedLocationManager];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
locationManager.distanceFilter = kCLDistanceFilterNone;
[locationManager startUpdatingLocation];
//Use the BackgroundTaskManager to manage all the background Task
self.shareModel.bgTask = [BackgroundTaskManager sharedBackgroundTaskManager];
[self.shareModel.bgTask beginNewBackgroundTask];
}
The above is only a snippet of the complete solution. I have shared a complete solution that is uploaded to Github and also a blog post on how to continuously get location update in the background for iOS 7 here: Background Location Services not working in iOS 7 .
Yes, if you fail to add following warning text in your app description and you have enabled background-location mode, Apple will reject your app.
Continued use of GPS running in the background can dramatically decrease battery life.

StartUpdateLocation during a didreceiveremotenotification:fetch

Good evening everyone !
I have a simple question : How to enable [CLLocationManager startUpdateLocation] when I receive a correct push notification using didReceiveRemoteNotification:fetch on iOS 7 ?
Right now, I have :
- (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
if ([userInfo objectForKey:#"aps"] && [[userInfo objectForKey:#"aps"] objectForKey:#"content-available"])
{
if ([userInfo objectForKey:#"update-location"])
{
[self performSelectorInBackground:#selector(handleLocationNotificationPush:) withObject:completionHandler];
}
if ([userInfo objectForKey:#"update-sensors"])
{
}
}
}
-(void)handleLocationNotificationPush:(void (^)(UIBackgroundFetchResult))completionHandler
{
[CLController.locMgr startUpdatingLocation];
++nbPushReceive;
[self.pushLockForLocation lock];
if ([self.pushLockForLocation waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:25]] == TRUE && self.lastKnownLocation != nil)
{
[self.pushLockForLocation unlock];
// Send my new location to server using HTTP request
[self sendLocationForPushUpdates:self.lastKnownLocation fetchCompletionHandler:completionHandler];
if (![UIApplication sharedApplication].applicationState == UIApplicationStateActive)
// it stops location updates
[self stopAllLocationUpdates];
return;
}
// In case we didn't receive any new position during 25 secondes
[self.pushLockForLocation unlock];
if (![UIApplication sharedApplication].applicationState == UIApplicationStateActive)
{
[self stopAllLocationUpdates];
}
completionHandler(UIBackgroundFetchResultNoData);
}
- (void)locationUpdate:(CLLocation *)location
{
NSLog(#" *** LocationContrller - LocationUpdate location");
self.lastKnownLocation = location;
if (location.horizontalAccuracy < 500)
{
[self.pushLockForLocation signal];
}
}
Of course, my CLController delegate is the same class (location updates work when application is in foreground). In my plist.file, I have the "Remote notifications" checked.
I am missing something ?
Thanks for your help ! :D
This could be related to the fact that in iOS7 enabling Location Services whilst in the background does not give you unlimited background processing time as it did in previous iOS version. Check out the WWDC 2013 What’s New in Core Location video at around 5 minutes 30. Therefore your app gets suspended again around 30 seconds after receiving the push notification.
I have a similar problem which as yet I've not found the solution to. However if you'd like to get to the same place I am try the following -
Firstly put an NSLog in didReceiveRemoteNotification, run your app on the device, put it into the background and send it a push notification. If you see your NSLog you'll know that the content-available flag is set correctly in your push.
Next add an NSLog in -(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations. If this gets hint then you know that you are indeed enabling location services.
If you get this far you've probably got the same problem I have. 30 seconds isn't always long enough to get a location to the accuracy I want it.
BTW if you're using an iPhone 5 or newer you can use deferred location and keep location services running constantly. Sadly I need it to work on an iPhone 4s.
UPDATE -
I've now found that this is specifically related to background push notifications and didReceiveRemoteNotification. Enabling Location Manager from a standard background task will work as it did before iOS7. Therefore you can still use background tasks together with Location Manager to get your position every x minutes, you just can't start the whole thing off using a background push.
Answering to myself, and using severals stackoverflow's posts, it's apparently impossible to re-active the location update when you're in background. To keep being updates of your location updates, you have to let it run even if you're going in background !!
:)

Resources