GPS sign doesn't go away when I kill all apps - ios

I have an app that uses GPS location. However I got a feedback that closing the app doesn't stop GPS. A user complained about iOS 7.1 saying that before iOS update the app was not doing that. Since I didn't change code, I wonder if I have to in order to satisfy iOS7.1 needs.
Here is what I do:
CLLocationManager *_lm;
- (void)mode:(LocatorMode)mode
{
NSLog(#"Location mode: %d", mode);
switch (mode) {
case lmBackground:
[_lm stopUpdatingLocation];
[_lm startMonitoringSignificantLocationChanges];
break;
default:
[_lm stopMonitoringSignificantLocationChanges];
[_lm startUpdatingLocation];
break;
}
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
[[Locator defaultLocator] mode:lmPrecise];
}
- (void)applicationWillResignActive:(UIApplication *)application {
[[Locator defaultLocator] mode:lmBackground];
}
Here is a screenshot I've made. Notice that there is no single app running and yet GPS sign is lit (a little arrow in top right corner).

Related

Why Location Permission Popup not coming after Reinstallation of iOS App?

I am facing one strange issue while using CLLocationManager for fetching the current Location. When I request for current location first time I am getting the permission popup having Allow and Don't Allow options. I have choose Don't Allow and then I went to App Settings and Turn On Location by choosing While Using the App and come back to the app and Uninstall the app. When I Reinstall the same ipa file I am not getting permission popup again.
Observation: When I was debugging the app I came to know that When App was installed first time and requested for Location Update I was receiving AuthorizationStatus = kCLAuthorizationStatusNotDetermined and from there I was calling requestWhenInUseAuthorization but when I was reinstalling the app instead of kCLAuthorizationStatusNotDetermined AuthorizationStatus i am directly getting requestWhenInUseAuthorization. I have observed this issue in iPhone 7 Plus with iOS 10. I have added permission keys into my info.plist file.
Please find the code snippet which i have used in the app.
- (Void)getCurrentLocation {
// Check if location services enabled in settings..
if ([CLLocationManager locationServicesEnabled]) {
locationManager = [[CLLocationManager alloc] init];
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
locationManager.delegate = self;
}
else{
NSLog(#"Location Services Turn Off");
}
}
[1]: https://i.stack.imgur.com/vo4An.png
#pragma mark- CoreLocation Delegate Methods
// This will be called when app level location permissions are changed.
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status{
NSLog(#"didChangeStatus called.. status: %d", status);
switch (status) {
case kCLAuthorizationStatusNotDetermined:{
NSLog(#"kCLAuthorizationStatusNotDetermined");
[locationManager requestWhenInUseAuthorization];
}
break;
case kCLAuthorizationStatusRestricted:{
NSLog(#"kCLAuthorizationStatusRestricted");
}
break;
case kCLAuthorizationStatusDenied:
{
NSLog(#"kCLAuthorizationStatusDenied");
}
break;
default:{
[locationManager startUpdatingLocation];
}
break;
}
}
// This delegate method will be called in case of iOS 6 or later when location data is fetched successfully.
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations{
CLLocation* receivedLocation = [locations lastObject];
[self formatLocationData:receivedLocation];
}
Please check and Let me know what should I do to get the Location Permission Popup while Re-installation of same ipa file.

Detect if app is opened from lock screen

I'm creating an app that displays monitors iBeacon regions, which means that when the device is locked and within range of a beacon, my app's icon is shown on the lock screen in the bottom left.
Is there any way that I can detect if my app is launched through this lock screen icon, and run some code?
When the icon appears, it indicates that a beacon region has been entered. The CLLocationManager delegate's didEnterRegion method will be called when the icon first appears.
You can put custom code there to set a flag indicating the icon appeared.
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
_iconShown = YES;
}
Then, when your app comes to the foreground, you can check this flag to see if it is set, and execute your custom logic:
- (void)applicationWillEnterForeground:(UIApplication *)application {
if (_iconShown) {
_iconShown = NO;
// Put custom logic here for launching from the icon
}
}
hmm, there is an alternative way, that you can handle it, just create an variable self.backgroundedToLockScreen like:
- (void)applicationWillEnterForeground:(UIApplication *)application {
if (self.backgroundedToLockScreen) {
... // app was backgrounded to lock screen
} else {
... // app was backgrounded on purpose by tapping the home button or switching apps.
}
self.backgroundedToLockScreen = NO;
}
and
- (void)applicationWillEnterForeground:(UIApplication *)application {
UIApplicationState state = [[UIApplication sharedApplication] applicationState];
if (UIApplicationStateInactive == state || // detect if coming from locked screen (iOS 6)
self.backgroundedToLockScreen) // detect if backgrounded to the locked screen (iOS 7)
{
... // app is coming from or was backgrounded to lock screen
} else {
... // app was backgrounded on purpose by tapping the home button or switching apps
}
self.backgroundedToLockScreen = NO;
}
Hope this could be useful for you.

Location Services not working in background called by WatchKit

My Apple Watch app requires some data and requests it from the corresponding iPhone app. To fulfill the request the iPhone app requires the users location.
After receiving and testing with a real Apple Watch I found out that my iPhone app does not receive location updates when running in background. If the iPhone app is active in foreground it works without issues. With the simulator it worked in both cases.
In both cases (active and background) the WatchKit extension calls and starts successfully the iPhone app and goes all the way until startUpdatingLocation is called in the iPhone app. But in case the app is running in background didUpdateLocations is never called.
I tried with requestAlwaysAuthorization as well as requestWhenInUseAuthorization. No difference.
I also activated then the "location updates" background mode within capabilities. But again no difference.
Has someone else faced the same problem and found a way to receive the location also in background?
Here some code. First the check if authorization is required.
// iOS 8 check to avoid crash on older iOS
if ([self.locationManager respondsToSelector:#selector(requestWhenInUseAuthorization)])
{
[self requestLocationAlwaysAuthorization];
}
else
{
[self runLocationUpdate];
}
Here the check for the proper Location Manager rights.
- (void)requestLocationAlwaysAuthorization
{
CLAuthorizationStatus currentAuthStatus = [CLLocationManager authorizationStatus];
if (currentAuthStatus == kCLAuthorizationStatusDenied)
{
//request user to change setting
}
else if (currentAuthStatus == kCLAuthorizationStatusRestricted)
{
//request user to change setting
}
else if (currentAuthStatus == kCLAuthorizationStatusNotDetermined)
{
[self.locationManager requestAlwaysAuthorization];
[self runLocationUpdate];
}
else if (currentAuthStatus == kCLAuthorizationStatusAuthorizedWhenInUse)
{
//maybe when in use is also enough?
[self runLocationUpdate];
}
else if (currentAuthStatus == kCLAuthorizationStatusAuthorizedAlways)
{
//all ok
[self runLocationUpdate];
}
}
Here the call of startUpdatingLocation. The didUpdateLocations delegate will only be called when iPhone app is active.
-(void)runLocationUpdate
{
[self.locationManager startUpdatingLocation];
}
Three things to check and be aware of:
Location Permissions like [self.locationManager requestAlwaysAuthorization]; are only acknowledged once by the OS. If you have already requested permission, doesn't matter the level, the OS will NOT display a request to the user. The OS will just pass over the request and leave the permission level as is. The only time you can be assured that the OS will display the request to the user is if the [CLLocationManager authorizationStatus] returns kCLAuthorizationStatusNotDetermined. In every other case, you must manually request permission by displaying an Alert or other form of UI display. Also note that the OS retains whether or not it already displayed the request, even if you delete your app and reinstall it. So to test, you need to reset your Simulator's Content or your iPhone's Location Privacy.
Make sure you have added the plist keys for NSLocationAlwaysUsageDescription AND NSLocationWhenInUseUsageDescription If you don't add this to your plist, the OS will ignore any Location Permission Requests.
If you want to use requestAlwaysAuthorization to get location data from the phone (not the watch app extension) while the phone app is in the background, will also require you register for Background Modes Location updates under Project>Target>Capabilities.
UPDATE
Use a background task to give your app time to respond when in the background. Something like this:
-(void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *replyInfo))reply{
UIApplication *app = [UIApplication sharedApplication];
UIBackgroundTaskIdentifier bgTask __block = [app beginBackgroundTaskWithName:#"watchAppRequest" expirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
//make your calls here to your tasks, when finished, send the reply then terminate the background task
//send reply back to watch
reply(replyInfo);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[app endBackgroundTask:bgTask];
bgTask=UIBackgroundTaskInvalid;
});
}

iOS Background app : best way to run app

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.

deffered Location updates are not available

I am trying to fetch user locations in foreground & background. I have to call api after I got a locaion update. To work in background I want to use Deferred method. I followed the same process as described in Apple WWDC. I am checking app on iPhone 5 (iOS 7). It is working fine when I am in foreground but did not give me update after I send the app into background. Below is the code which I am using to get location in background.
#import "AppDelegate.h"
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.locationArray = [[NSMutableArray alloc] init];
self.locationErrorArray = [[NSMutableArray alloc] init];
self.manager_loc = [[CLLocationManager alloc] init];
self.manager_loc.activityType = CLActivityTypeFitness;
self.manager_loc.delegate = self;
[self.manager_loc setDesiredAccuracy:kCLLocationAccuracyBest];
[self.manager_loc startUpdatingLocation];
return YES;
}
- (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.
}
- (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.
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
- (void)applicationWillTerminate:(UIApplication *)application
{
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
#pragma mark - Location Manager Delgate
-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(#"update failed");
}
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
[self.locationArray addObject:locations];
NSLog(#"udate locations %f %f", manager.location.coordinate.latitude, manager.location.coordinate.longitude);
if (!self.deferredStatus)
{
self.deferredStatus = YES;
[self.manager_loc allowDeferredLocationUpdatesUntilTraveled:100 timeout:30];
}
[self.manager_loc stopUpdatingLocation];
}
-(void)locationManager:(CLLocationManager *)manager didFinishDeferredUpdatesWithError:(NSError *)error
{if (manager.location != nil)
{ [self.locationArray addObject:manager.location];
}
if (error != nil)
{
[self.locationErrorArray addObject:error.description];
}
self.deferredStatus = NO;
NSLog(#"deffered success %f %f", manager.location.coordinate.latitude, manager.location.coordinate.longitude);
}
#end
If I do not stop the location update in didUpdateToLocations Delegate then the location arrow (on status bar) do not go. In that case it gives me locations contionusly. I want location update after a particular time or particualar distance travelled, so that I can hit server with the user locations. Please help me on this.
Use LocationManger distanceFilter Property for update location at particualar distance travelled.
self.manager_loc.distanceFilter= 100;// In meters
If you want location updated in Backggriound then register your for background updates. Youca can do it in plist.
Set location manager to :
if ([self.locationManager respondsToSelector:#selector(pausesLocationUpdatesAutomatically)]) {
self.locationManager.pausesLocationUpdatesAutomatically = NO;
}
Then If you want to location updated after some time or distance then use:
- (void)allowDeferredLocationUpdatesUntilTraveled:(CLLocationDistance)distance
timeout:(NSTimeInterval)timeout // No guaranty it will work exactly or not
If you want location updated based on distance the you can use
Desired accuracty and distanceFilter property.
self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;// Use other accurarcy as your need
self.locationManager.distanceFilter = 3000; //100, 200, etc
If you set activity type to CLActivityTypeFitness all above setting will overrided, And location manager updated according to this activity, which is as per my knowledge will eat Battery.
While using CLLocation Manager one thing you should accept it will not give all updartes 100% accurate.
See my answer for this post: StartUpdateLocations in Background, didUpdatingToLocation only called 10-20 times
If you need location updates in the background under iOS 7, you must call startUpdatingLocation while your App is in the foreground. You can no longer do this while your App is in the background, so you can no longer register for location updates only when you need them and while you need them. You are forced to register for them for the whole time your App is running (in the foreground and the background) and so you’re forced to waste a lot of energy.
You can reduce the battery usage a little bit by setting the accuracy to kCLLocationAccuracyThreeKilometers when you do not need the location updates and set them to kCLLocationAccuracyBest only when you need the updates. But this will nevertheless drain the battery faster than expected.
Please write a bug report to Apple and ask for the „old" behavior of iOS 4,5 and 6, where you could call „startUpdatingLocation“ in the background as well to get location updates in the background. If Apple gets enough requests to change this behavior back to the way it was implemented in iOS 5/6, the more likely it is that Apple will change this back.
The currents situation is really bad. Bad for developers, which are forced to waste energy, or to abandon their Apps, bad for the user, whose device needs to be plugged to a power source much earlier, or who can no longer use certain Apps.

Resources