I have an iOS app that uses the CLLocationManager to monitor regions and to get GPS updates. Most of the time, I want my app to continue tracking the cellphone when it goes in background or even when it gets killed, and it works well (I can still see the small arrow in the status bar after my app gets killed). The problem is that I am not able to stop monitoring the regions and GPS updates after my app has been restarted by the Location Services.
When my app gets restarted by the Location Services, I instanciate the CLLocationManager and then call its methods stopRangingBeaconsInRegion and stopUpdatingLocation before setting its delegate to nil and itself to nil.
Thanks to NSLogger, I can see that my callbacks are no longer called, but the small arrow in the status bar stays there (and my app is the only one that I allowed to use the Location Services from the settings menu).
What did I miss? Is there a way to know what still uses the Location Services in my app?
When you call stopRangingBeaconsInRegion, where are you getting the list of regions? The proper way to do this is like below:
for (CLRegion *monitored in [self.locationManager monitoredRegions]) {
NSLog(#"Stopping monitoring on: %#", monitored.identifier);
[self.monitoringLocationManager stopMonitoringForRegion:monitored];
}
for (CLBeaconRegion *region in [self.locationManager rangedRegions]) {
NSLog(#"Stopping ranging on: %# ", region.identifier);
[self.rangingLocationManager stopRangingBeaconsInRegion:region];
}
I finally found that I missed to remove some of my numerous regions. The easy way to avoid this mistake is to retrieve the list of regions monitored with the property monitoredRegions and call stopRangingBeaconsInRegion for each of them. I also forgot to call stopMonitoringSignificantLocationChanges (I didn't know that my app was using it, since I am modifying the app of a former colleague).
How do you know that it is your app that is using the location services?
The small arrow appears if the iOS itself is using the location services in the background.
if you did call stopRangingBeaconsInRegion and stopUpdatingLocation before setting the delegate to nil and you are not getting any callbacks then your app is not using the location services.
Related
I am trying to make an app to track the user GPS all the time, this app is a kind of car GPS tracker to get the location of driver all the time and send it to server.
I have tried to add "location updates" to the "background modes" but the app will automatically suspends after 10 mins when going into background.
Is there a way to make this app run all the time and get the GPS location?
You have two options here:
1) Regular location tracking. This type of tracking works with kCLAuthorizationStatusAuthorizedWhenInUse and kCLAuthorizationStatusAuthorizedAlways authorizations. When CLLocationManager started tracking location once it will receive location updates in delegate method locationManager:didUpdateLocations:. App can go to suspended state, but when location manager receive new location app goes to background state and handles new location in delegate method. How to setup location manager:
- (void)viewDidLoad {
[super viewDidLoad];
self.locationManager = [[CLLocationManager alloc] init];
// Setup location tracker accuracy
self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
// Distance filter
self.locationManager.distanceFilter = 50.f;
// Assign location tracker delegate
self.locationManager.delegate = self;
// This setup pauses location manager if location wasn't changed
[self.locationManager setPausesLocationUpdatesAutomatically:YES];
// For iOS9 we have to call this method if we want to receive location updates in background mode
if([self.locationManager respondsToSelector:#selector(allowsBackgroundLocationUpdates)]){
[self.locationManager setAllowsBackgroundLocationUpdates:YES];
}
[self.locationManager startUpdatingLocation];
}
2) Signification location changes tracking. This type of tracking works only with kCLAuthorizationStatusAuthorizedAlways authorization. It receives new location only each 500 meters, so distance filter and desiredAccuracy don't work here. App can go to suspended state, and even can be terminated by system, but when location updates app goes to background state and receives location in delegate method locationManager:didUpdateLocations:.If app was terminated by system, it will be relaunched in background with UIApplicationLaunchOptionsLocationKey key in launch options in didFinishLaunchingWithOptions app delegate method. How to setup this type on tracking:
- (void)viewDidLoad {
[super viewDidLoad];
self.locationManager = [[CLLocationManager alloc] init];
// Assign location tracker delegate
self.locationManager.delegate = self;
// For iOS9 we have to call this method if we want to receive location updates in background mode
if([self.locationManager respondsToSelector:#selector(allowsBackgroundLocationUpdates)]){
[self.locationManager setAllowsBackgroundLocationUpdates:YES];
}
[self.locationManager startMonitoringSignificantLocationChanges];
}
You should notice that both of these methods does not guarantee that your application does not go to suspended state.
Also, if app was terminated by user (for example from app switcher by swipe) location tracking in background will not work.
UPDATE (corresponding to comments)
Here is my code examples that work for me:
For Regular tracking. Run the example, provide access to user location, tap Start button to start location updates. To test locations in simulator choose in simulator menu Debug > Location > Freeway Drive. Now you can push app to background by home button (Command+Shift+H). Leave simulator for more than 10 minutes, and all this time app will receive locations. When you return to app you will see red pins on the map.
For Significant changes. Run the app and test by the same way as for previous example.
Monitoring Significant changes can be started only by method [self.locationManager startMonitoringSignificantLocationChanges];
UPDATE (iOS 11)
Changes to location tracking in iOS 11
iOS 11 also makes some major changes to existing APIs. One of the affected areas is location tracking. If your app only uses location while the app is in the foreground, as most apps do, you might not have to change anything at all; however, if it’s one of those apps that continuously track user’s location throughout the day, you should probably book some time this summer for making some changes in how you do the tracking and testing possible usage scenarios.
follow this link: https://mackuba.eu/2017/07/13/changes-to-location-tracking-in-ios-11/
I am sure it is useful for the author because the question was asked in Feb 2016 and I am giving an answer in June 2019. This answer maybe is useful for other users.
Recently, I was working with the same requirement. After 2-3 week hard work, I did it. For other users, I create a helper class for it. Which is available in GitHub.
Please use HSLocationManager for your requirement. I have achieved the same requirements in one of my project
Location manager that allows getting background location updates every
n seconds with desired location accuracy.
Advantage:
OS will never kill our app if the location manager is currently
running.
Give periodically location update when it required(range is between 2 -
170 seconds (limited by max allowed background task time))
Customizable location accuracy and time period.
Low memory consumption(Singleton class)
In reply to comment 1 in the solution (I can't comment anywhere yet): you didn't seem to solve the problem as your app gets suspended and doesn't update the location any more after 10 minutes.
I had the same issue: I had set setAllowsBackgroundLocationUpdates to YES, and I had the NSLocationAlwaysUsageDescription key in my Info.plist, but my App also used to stop tracking location after 10 minutes.
I solved it by adding both NSLocationAlwaysUsageDescription and NSLocationWhenInUseUsageDescription to the Info.plist file so it looks like this:
<key>NSLocationAlwaysUsageDescription</key>
<string>This app needs to use your location</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs to use your location</string>
set it.it take power of battery but your application run in Background.OS does not Suspend your App
[self.locationManager setPausesLocationUpdatesAutomatically:NO];
Hi i am developing an application with beacons i need to scan for beacons continuously even though app is closed. is there any cordova plugin which is able to run in background, please suggest me sample applications tutorials
Background modes are only a partial answer. You need to have one of or both entries in Info.plist:
Then in some place in your code you need to ask for requestWhenInUseAuthorization or in your case requestAlwaysAuthorization. Then you need to handle this in location manager delegate. I'm doing it like so:
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
if (status == kCLAuthorizationStatusAuthorizedWhenInUse) {
[self startMonitoringAllRegions];
[self.locationManager startUpdatingLocation];
}
else if (status == kCLAuthorizationStatusNotDetermined) {
[self.locationManager requestWhenInUseAuthorization];
}
}
So somewhere i'm asking to start monitoring for a beacon region then in this method I'm checking if I have permission from the user to do that if not I'm requesting if (the string from the plist will be displayed) if I have I'm starting updating user location.
Hope that will get you going :D
This is possible with native iOS code, but not with Cordova. When you say "scan" beacons, this usually means using the CLLocation ranging APIs, which provid updates of all visible beacons every second. In order to do this continuously in the background, you need to:
Add location as a background mode in your app's .plist filke in XCode as #hasan83 suggests.
Start a background thread to keep your app running in the background.
Request requestAlwaysAuthorization as #sloik suggests.
I wrote a tutorial and reference app of how to do this. The tutorial mentions this working only for 3 minutes, but with the background mode of location, the 3 minute restriction goes away. Be aware that adding this background mode may make it more difficult to get your app approved for the AppStore. Apple must approve of the reasons for this background use of location services.
The reason you can't do this with Cordova is because JavaScript must run to collect the beacon data returned by the ranging APIs. This works in the foreground, but not in the background, because Cordova relies on a WebView being in the foreground to execute JavaScript code. In the background, your custom code just cannot run.
Allow UIBackgroundModes for location updates in the project plist
file.
Select the project at the left pane, then select Capabilities and expand the Background Modes list and select the Location updates option:
Try this plugin for using iBeacons. There seem to be a few plugins for keeping apps running in the background, e.g. this one. I doubt you'll find both functions in the same plugin.
I've implemented the use of GeoFences in my app. I've created a new CLLocationManager property and initialised it in my app's viewDidLoad method simply like so:
[[self.locationManager alloc] init];
I set the delegate to self, start monitoring for regions using startMonitoringForRegion:
Then, I emulate my location while running it using Xcode and the methods didEnterRegion and didExitRegion. The app works perfect while running, but I haven't got the chance to test it as I don't know how to emulate my location while the app is terminated, see my other question
Therefore, I was hoping to get some answers on these questions:
Can I manage background work just like normal with the didEnterRegion and didExitRegion methods? Like calculating, etc?
If my app is terminated, I enter a region, open my app - are my variables from didEnterRegion initialised and set up then?
Do I need to do anything else to set it up to work when my app is terminated, except for the normal CLLocationManager setup that I've done so far?
Thanks!
My CLLocationManager starts when the user first enters the app. I am initializing it like this:
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.distanceFilter = kDistanceFilter;
self.locationManager.headingFilter = kHeadingFilter;
And I am using geofencing.
I have defined in my .plist the required background modes.
If I test the app in the simulator using a gpx file, it works fine. So far, so good.
However, I read in these two links:
Start Location Manager in iOS 7 from background task
Background Location Services not working in iOS 7
That in iOS7, when the location does not change for a couple of minutes then the CLLocation goes to sleep.
My question is that:
I do not call didUpdateLocation at all, I only want the didEnterRegion, or didExitRegion. Theoretically, will it work , even the app is killed or user Location has not changed in the last hour and then decides to move?
There are a few elements in your questions:
1- In order not to rehash what is in a previous answer, I would first look at my answer at this link. It will help you resolve around the core location updates stopping if the user stops moving and how you can modify that behaviour:
iOS7 Core Location not updating
2- If the user kills an app in iOS7 (by flicking it up in the app switcher), the app will not restart and as such neither your location updates nor region monitoring will restart after the phone is restarted. On the other hand, if the app is killed because of memory pressures, iOS will restart the app and relaunch your location updates and region monitoring in the background. Also, if you reboot the phone, region monitoring will restart automatically. You obviously must have the background mode enabled.
3- Now you are talking about regions in your questions so I am assuming you have implemented CLCircularRegion. If not, that should be the first step and then "startMonitoringForRegion". In your case, even if the user has stopped moving for a while and then started moving, the app will be awaken/delegate called when the app enters or exit a region.
4- Make sure to use locationManager: didDetermineState: forRegion: as well. This will allow the app to determine if it is inside or outside of the region at start. This is useful when you think of an app being launched and no region crossing has happened but it is important to know whether the user is inside or outside of the region.
Hope this helps
I am failing to find any documentation on this. If my application is able to use background location services, how much processing can it really do before the device stops the task as its using too much resource?
E.g. When the location changes, I would like to ping my server with the new location.
Regards:
John
If an app has the permission of getting location updates in the background, then you are not limited on performing any tasks, so you could run any code, as the app is allowed to execute code while it is suspended (this is used by apps providing navigation).
However, be aware, that Apple will not approve your App, with the background Location updates permission, unless it's an App that would justify this requirement (Sports tracking App, Navigation App, etc)
You can get location updates from Region Monitoring or Significant Location changes, without having to use the location updates background mode.
So now comes the tricky part. Do I have enough time to perform a Server update of the users Location when my App is started from Background mode by the Region Monitoring or Significant Location services ?
The answer, seems to be yes, as suggested by this article
I haven't tried this, but judging by the code, I don't find a reason why this shouldn't work!
You will have to declare App’s Supported Background Tasks in info.plist
Under the information property list add array given format below:
Inforrmation Property list (Dictionary) ->
Required background modes (array) (1 item) ->
item 0 (String) - App registers for location updates
after these settings in info.plist you can do code which you want to do in background process at AppDelegate:
- (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.
*/
self.locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
[_locationManager stopMonitoringSignificantLocationChanges];
[_locationManager startUpdatingLocation];
}
For more information you can visit:
Declaring Your App’s Supported Background Tasks