We have an app that is totally dependent on constant location updates and found that when the phone isn't getting gps, didUpdateToLocation isn't being called even if the device is connected to a wifi network. To further this test, I loaded the app on a 4th gen iPod Touch with the same results.
To clarify, the app workings perfectly on the iPhone 4 when GPS is available. We didn't notice the problem until the client tested it on his iPhone 4 in his office where he knows he doesn't get gps.
I thought that location manager was supposed to support wifi as a method to determine location, but this doesn't appear to be the case.
Here is my code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if (locationManager==nil) {
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
locationManager.distanceFilter = kCLDistanceFilterNone;
NSLog(#"Accuracy best: %f distance filter none: %f", kCLLocationAccuracyBestForNavigation, kCLLocationAccuracyNearestTenMeters);
}
[locationManager startUpdatingLocation];
...
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{....
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{....
As far as I thought, there isn't anything special that you have to do to make locationManager work over wifi - and now I find out it doesn't work at all.
BTW- My phone device is an iPhone 4 running iOS 5 beta 7 and an ipod touch iOS 4.1.
I have discovered that didUpdateLocation is being called... once, when the app first opens. It makes me think that when you're dealing with wifi only, location manager is in
startMonitoringSignificantLocationChanges mode only.
Try calling:
[self.locationManager startUpdatingLocation];
Possible you may need to configure your location manager parameters on self.locationManager as well.
It sounds as if your Wi-Fi Access Point is unknown. To make sure that it is not a coding error, in the simulator start Safari and see whether it finds your location. If it doesn't, the problem is your unknown Wi-Fi AP. The solution is to make your AP known to the outside wold. In order to do that, go to http://www.skyhookwireless.com and read the instructions. This site enables you to update your Wi-Fi Access Point information in the XPS system. Note: it took six weeks before I was hooked up, but it works now.
Related
Hi recently with the iOS 9 GM seed version out,I have seen location update(allowDeferredLocationUpdatesUntilTraveled:timeout:) not getting deferred. I am getting error kCLErrorDeferredFailed - The location manager did not enter deferred mode for an unknown reason.
Its because of this error its not entering the defer mode at all and location updates are firing continuously .
The same code used to work in iOS 8.4 and below versions.Its draining my device's battery by a huge percentage.
Is there anything we need to explicitly set or mention for iOS 9.Didn't find anything from Apple documentation?
-(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
self.deferringUpdates = NO;
}
I also set the allowsBackgroundLocationUpdates property but even that didn't help.
self.locationManager.allowsBackgroundLocationUpdates=YES;
In iOS 9 and later, regardless of deployment target, you must also set the allowsBackgroundLocationUpdatesproperty of the location manager object to YES in order to receive background location updates. By default, this property is NO, and it should remain this way until a time when your app actively requires background location updates.
Reduce Location Accuracy and Duration
Please let me know what additional I need to make
Thanks
Please read the discussion on the API reference page.
A few things to try, as mentioned here
Kill all apps from multitasking bar
Verify that no other app is using location services
Unplug the device from debugger and MacBook (Verify it is still getting call by logging didUpdateLocations to a file)
Make sure that the device supports deferred updates
Make sure that the distanceFilter is None, the desiredAccuracy is Best
I'm starting on IOS developing, and I'm very unfamiliar with the language.
I'm trying to develop an app that will track the location of a device on the background. I've followed some tutorials and put up a code that Logs the location update.
-(void)CurrentLocationIdentifier
{
locationManager = [CLLocationManager new];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
NSLog(#"%#", newLocation);
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[self CurrentLocationIdentifier];
}
It works. Since I've configured the .plist file it is registered for running on background, it logs the location endlessly.
But I need the update to be periodically, with a defined interval, otherwise it would be a battery killer. I realize that there are tons of questions here on SO about Location Update on the background, but my attempts are not being successful and I'm stuck.
I've tried stoping the Update and scheduling a start with NSTimer within the location update listener:
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
NSLog(#"%#", newLocation);
[locationManager stopUpdatingLocation];
NSTimer *timer;
timer = [NSTimer scheduledTimerWithTimeInterval:10
target:self
selector:#selector(CurrentLocationIdentifier)
userInfo:nil
repeats:NO];
}
Where CurrentLocationIdentifier is a method that requests the start update again.
But it runs the first time, and then don't fire the scheduled task! What am I doing wrong? Should it be done some other way?
If you don't need detailed location updates in the background then the significant location update service is probably the best approach.
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
... // Any other initialisation you already have
locationManager = [CLLocationManager new];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
if ([CLLocationManager significantLocationChangeMonitoringAvailable] {
[locationManager stopUpdatingLocation];
[locationManager startMonitoringSignificantLocationChanges];
}
else {
//Decide what you want do to if it isn't available
}
}
- (void)applicationDidEnterForeground:(UIApplication *)application
{
[locationManager startUpdatingLocation];
[locationManager stopMonitoringSignificantLocationChanges];
}
By the way, the delegate method you are using, locationManager:didUpdateToLocation:fromLocation: is deprecated and you should use locationManager:didUpdateLocations:
UPDATE
Significant location change isn't available on all devices, so I added a check to my code. You need to decide what approach to take on a device where it isn't available - stick with standard updates, perhaps increasing distanceFilter
I have a project on Github, https://github.com/dsdavids/TTLocationHandler, that you are welcome to download and fiddle with. TTLocationHandler is a drop in class that you can set as you wish to get the sort of results others have been pointing to. I made it when I was in exactly your situation, trying to figure out how to use location services in the background.
I would strongly advise that you forget any ideas of trying to fire regularly by any scheduled timer. The idea is to set the handler to fire for events as makes sense then analyze the return and further filter down your response to only as much activity as needed. I find significant location changes to be the best trade off.
You are able to set this for tracking non stop, then respond only as often as you require. You better have a good reason for that battery hit or your app will be rejected. My own app was rejected at first, even though I was setting it to only go full bore if it was plugged in to power. I suppose they were right though. I was able to get everything I need with significant location changes and region monitoring. My app never lets the locationManager go wild and has little impact on battery.
There is no way to wake up the application after a given time interval to execute your code. You really have two options, background fetches, and either region monitoring or significant location changes.
In terms of getting location updates periodically (based on time rather than distance moved), the best you can do is utilize iOS multitasking API performFetchWithCompletionHandler, which in a nutshell allows the OS to wake you up when it determines you can have time to process. You'll have about 30 seconds of wall time to handle what you want to and go back to sleep.
https://developer.apple.com/library/ios/documentation/uikit/reference/uiapplicationdelegate_protocol/index.html#//apple_ref/occ/intfm/UIApplicationDelegate/application:performFetchWithCompletionHandler:
There are a number of steps involved to get this set up, but it's pretty easy and should point you in the right direction. Note that this will not allow you to schedule these events whenever you want, you are really limited by the OS and the algorithm it uses to determine how often you should be allowed to perform background fetches. If you take a long time and use lots of battery, you will get less frequent fetches, and if you take too much time and never complete you'll be terminated. Being "responsible" in the background ends up with the OS giving you more time.
In my experience, being as "responsible" as possible while doing location updates and sending server requests, background fetches occur approx. 15-30 minutes apart.
If you want to be woken up when users move some specified distance, region monitoring is a pretty good (although imperfect) way to handle that, and can allow your app to wake up, start a long running background task (30 seconds) and do what you need to do before ending the task and going back to sleep.
I am looking into using deferred location updates for an iOS activity tracker, which allows location services in background. I've implemented the suggested code snippets (see below). In Xcode debugging, deferred locations attempt to start a few times until location data comes in at about 1 per second. After that, it claims to succeed in starting deferrals, and the callback for the finish trigger also succeeds after the specified time period expires. However during the time, the location handler still runs once per second. I've read that this is because the phone hasn't deemed itself ready to enter the background, and that testing in Xcode does this. Note, AppDelegate's "didEnterBackground" eventhandler got called immediately when turning off the screen, and resumed when reopening app.
I ran the same code with the phone disconnected as another test, near the window with GPS, screen off, or switching to entirely different apps, and it still never actually defers the updates. I can tell because the networked update still comes in once every 30 seconds, instead of the interval of 120 seconds which is desired in the code sample below.
What else is needed to actually get deferrals to work, since there is no error occurring in starting them and they do get their finish callback? Why do location updates continue at 1 per second even when the app goes to background?
Iphone 5s, IOS 7.1.1
// .h file (partial)
#interface MotionTracker : NSObject<CLLocationManagerDelegate, UIAccelerometerDelegate>
#property (strong, nonatomic) CLLocationManager *locationManager;
#end
// .m file (parial)
- (id) init {
if(self = [super init]){
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
_locationManager.distanceFilter = kCLDistanceFilterNone;
_locationManager.desiredAccuracy = kCLLocationAccuracyBest;
// if set to YES (default), app stops logging location at some point and doesn't resume in any timely fashion, all data points lost in between
_locationManager.pausesLocationUpdatesAutomatically = NO;
_locationManager.activityType = CLActivityTypeFitness;
}
return self;
}
// called early in program after login confirmed
- (void) startCollectingLocation {
[_locationManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations {
// logs to file when device is not in debug
// always returns 1
NSLog(#"Location update count: %d",[locations count]);
// some code here to handle location updates
// - collect key location day in NSDictionary
// - every N seconds send Network call to server to save (have tried 30 seconds, 15 minutes, 30 minute network intervals). Have also tried turning off network calls completely.
// deferred updates starter
if (!self.deferringUpdates) {
if([CLLocationManager deferredLocationUpdatesAvailable]){
[_locationManager allowDeferredLocationUpdatesUntilTraveled:500 timeout:(NSTimeInterval)120]; // (have also tried large numbers, and "Infinite"
self.deferringUpdates = YES;
NSLog(#"Deferred updates start");
} else {
NSLog(#"Deferred updates not available");
}
}
}
- (void)locationManager:(CLLocationManager *)manager didFinishDeferredUpdatesWithError:(NSError *)error {
if(!error){
_deferringUpdates = NO;
NSLog(#"Deferred updates: finished");
} else {
_deferringUpdates = NO;
NSLog(#"Deferred updates: %#", [error localizedDescription]);
}
}
If the device is connected to a debugger or on a charger, the device will remain powered (not sleep) and therefore will not enter deferred mode. Deferred mode is a power optimization allowing the device to sleep. If the device is not scheduled to sleep for other reasons, enabling deferred mode will not force it to sleep otherwise. Try your test by ensuring no other apps are using location services, and disconnecting it from a charger with the screen off. After running for some time, plug back in and check your logs, you should see that the device slept and deferred updates.
From Apple's
allowDeferredLocationUpdatesUntilTraveled:timeout: documentation:
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.
It is also worth noting that deferred updates are only available when locationManager.desiredAccuracy is set to kCLLocationAccuracyBest OR kCLLocationAccuracyBest; locationManager.distanceFilter must also be set to kCLDistanceFilterNone.
From Apple's documentation:
...the location manager allows deferred updates only when GPS hardware is available on the device and when the desired accuracy is set to kCLLocationAccuracyBest or kCLLocationAccuracyBestForNavigation.
and
...the distanceFilter property of the location manager must be set to kCLDistanceFilterNone.
I have been struggling with the same issue, and I may have found an answer that solves this problem for many - at least it solves my problem and gets deferred updates working consistently for me.
I followed all of the steps in this list and no matter what I did, location updates would not defer. It occurred to me that I might have other apps running that were not allowing the system to sleep, so I killed all other apps in the multitasking tray. I ran my sample app again and ... it worked!
But the story doesn't end there. I tried again a little later, and even though there were no other apps running in the multitasking tray I couldn't get location services to defer. Then it occurred to me that I have an app on my phone called "Moves" which manages to keep itself alive even after you manually kill it. I'm not entirely sure how Moves comes magically back to life when you kill it, but it does (perhaps using bluetooth and the app preservation / restoration service). Even though it is alive and tracking your location it doesn't appear in the multitasking tray. I think that only apps that are manually launched appear in the tray - if the OS launches an app it doesn't appear in the tray. But I digress ...
I was able to get deferred location services to work consistently in my app by disallowing Moves to use location services. When I did, Moves complained even though it wasn't in multitasking tray. It seems that if another app is using location services (and not deferring) your app won't defer either.
Hi recently with the iOS 9 GM seed version out,I have seen location update(allowDeferredLocationUpdatesUntilTraveled:timeout:) not getting deferred.The same code used to work in iOS 8.4 and below versions.Its draining my device's battery by a huge margin.
Is there anything we need to explicitly set or mention for iOS 9?Didn't find anything from Apple documentation
Here is the code that I implemented.
-(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 self.deferringUpdates = NO;
}
I also set the allowsBackgroundLocationUpdates property but even that didn't help. self.locationManager.allowsBackgroundLocationUpdates=YES;
In iOS 9 and later, regardless of deployment target, you must also set the allowsBackgroundLocationUpdatesproperty of the location manager object to YES in order to receive background location updates. By default, this property is NO, and it should remain this way until a time when your app actively requires background location updates.
https://developer.apple.com/library/prerelease/ios/documentation/Performance/Conceptual/EnergyGuide-iOS/LocationBestPractices.html
Please let me know what additional I need to make
Thanks
I got my app rejected because
We found that your app uses a background mode but does not include functionality that requires that mode to run persistently. This behavior is not in compliance with the App Store Review Guidelines.
We noticed your app declares support for location in the UIBackgroundModes key in your Info.plist but does not include features that require persistent location.
I need to track the user location in the background in order to fire a notification if the user is near one of his/her pictures on the application. That's why I used background mode key on my .plist
This how I initialize my locationManager
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
_locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
_locationManager.distanceFilter = 1000; // Will update evert 1 KM.
_locationManager.activityType = CLActivityTypeFitness;
_locationManager.pausesLocationUpdatesAutomatically = YES;
[_locationManager startUpdatingLocation];
And I'm using didUpdateToLocation to receive the location on the background.
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
if (_currentLocation == nil){
self.currentLocation = newLocation;
}else{
if([self.currentLocation distanceFromLocation:newLocation]){
[self getNearPicturesMethod:newLocation];
}
}
}
They pointed to me that I should use "startMonitoringSignificantLocationChanges" but still if I remove the background key (what they want) the app won't receive location updates anymore if it's on the foreground, they present only if the user opens it and that doesn't work for me.
PS. I read related questions but nobody seemed to fix this problem or to provide a workaround.
Your app will wake on significant location changes. You do not need the background location mode key set for that. Same goes for region boundary monitoring.
For your case, I think you will more likely want to set regions for the nearest pictures and then just wait for boundaries to be crossed.
Use significant location changes to update the nearest pictures and reset your regions to those.
This will work as intended, be within the guidelines of Apple docs and impact the battery of the user very little, if any.
Testing Device: iPhone 5 (iOS 7)
I have an app that uses RegionMonitoring and updateLocation. If a region is entered, didEnterRegion is called as expected. Then I call startUpdatingLocation. But the method didUpdateToLocation is only called 10-20 times while it should update the location until the timer fires.
The relevant code:
CLLocationManager *_locationManager;
NSTimer *_timer;
-(void)initLocationManager
{
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
[_locationManager setActivityType:CLActivityTypeOther];
[_locationManager setDesiredAccuracy:kCLLocationAccuracyBestForNavigation];
[_locationManager setPausesLocationUpdatesAutomatically:NO];
[_locationManager setDistanceFilter:kCLDistanceFilterNone];
}
//Did Enter Region, called as expected:
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
[_locationManager startUpdatingLocation];
_timer = [NSTimer scheduledTimerWithTimeInterval:300.0f target:self selector:#selector(scheduleMethod:) userInfo:nil repeats:NO];
}
//Timer Fire Method:
- (void) scheduleMethod:(NSTimer*)timer
{
[Utils writeToLog:#"Timer-Stop"];
[_locationManager stopUpdatingLocation];
}
//This Method only called 10-20 Times (in the first 10-20 Seconds) and not the complete 5 Minutes:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
[Utils writeToLog:#"LocationUpdate!"];
}
So far I tried:
Restarting the Updates in the locationManagerDidPauseLocationUpdates method, but it seems that this is never be called:
-(void)locationManagerDidPauseLocationUpdates:(CLLocationManager *)manager
{
[WRUtils writeToLog:#"LocationUpdate paused, restarted"];
[_locationManager startUpdatingLocation];
}
Check for errors in the didFailWithError method, but this is not called either.
And played a bit with the properties:
[_locationManager setActivityType:CLActivityTypeOther];
[_locationManager setDesiredAccuracy:kCLLocationAccuracyBestForNavigation];
[_locationManager setPausesLocationUpdatesAutomatically:NO];
Plist settings are right I guess:
Required background modes YES
Required background Modes
Item 0 App registers for location updates
How can I solve this?
Apple has introduced a new policy in iOS 7. iOS 7 does no longer deliver location updates in the background if you call „startUpdatingLocation“ while the App is in the background. You only get update when the App is brought to the foreground in this case.
When you’re using the geofencing feature, your App just gets a few seconds each time you get a RegionEntered/Exited notification to process this notification. And in these few seconds you may also get location updates. After these few seconds are over iOS 7 just suspends your App again.
You can use background task to get more than just a few seconds, but in iOS 7 Apple has also reduced the time Apps can run in the background from 10 minutes (iOS 6 and older) to only 3 minutes under iOS 7. It looks like that these 3 minutes are the total amount for the whole time the App is in the background. Which means you can not ask iOS 7 10 times to get 1 minute background time, you’ll only get 3 minutes in total, so after the 3rd time you’ve asked for a minute, your App won't get any background time anymore at all.
Your only chance in getting location updates in the background is to call „startUpdatingLocation“ while the App is in the foreground. This is sad especially when you only need location updates in response to Region(Enter/Exit messages, because you need to let the location updates running all the time. But you can at least reduce the battery usage by setting the accuracy value to kCLLocationAccuracyThreeKilometers when
you do not need location updates and set the accuracy to kCLLocationAccuracyBest only when you really need the geo coordinates. The iOS won’t power up GPS for the kCLLocationAccuracyThreeKilometers value, so the battery usage would be moderate in this case.
Also the value kCLLocationAccuracyBestForNavigation for the accuracy seems to cause problems under IOS 7. I do not get any location updates with this value if the device is not connected to a an external power supply.
All in all the new iOS 7 policy for location updates is making it much harder to develop certain kinds of Apps. Instead of registering for location updates only when needed, you are forced to register for these for the while lifetime of your App. Which of course drains the battery a little bit faster, though Apple’s intension for this new policy was probably the opposite.
UPDATE:
After some more testing I’ve found some kind of solution for the problem.
The docs from Apple mention that when using the Significant Location Change API, Apps can receive location updates in the background even if „startUpdatingLocation“ is started in the background.
My first tests didn’t work well. I was registering my App for significant location updates within the region monitoring delegate methods just before calling startUpdatingLocation (so this location service is only enabled when needed), but this still does not deliver location updates in the background as the docs would suggest.
But if you start listening for significant location changes directly after your App is launched (and never switch this off), you can call start „startUpdatingLocation“ while the App is in the background and also receive location updates in the background. The battery usage of having the "significant location change“ feature on all the time seems to be very low, so this will be probably the solution for most Apps.
You have to check if the „significant location change“ feature is available on the device, but it seems that all current devices do support this. Even the 5th generation of iPod Touch does support it (The iPod Touch can not use cell towers for location updates, which is the base method of this feature according to the docs from Apple, so I guess we can assume that all current devices running iOS 7 can use the „significant location update“ API. Though it’s probably a good idea to check if this feature is really available, maybe there are certain circumstances where the feature is not available).
Using the "significant location change“ API might have one disadvantage: The App can be launched in the background (if it was terminated in the background by the iOS to reuse its memory for other Apps) repeatedly and unnecessarily whenever the device has moved „significantly“ (according to the docs: when the cell tower has changed, but no more than once per 5 min). So Apps which only need to be activated, when a certain region is exited or entered, will be launched and informed about location changed all the time, not only at those regions. But I assume this should be still much better than having standard location updates active all the time.
My iPhone 5s drains the battery only 1% over night with the significant location changes active, instead of 12% with having the standard location updates active with the accuracy set to 3km.
Hope this helps all developers who are struggling with the new iOS 7 behavior.
In background timer will not run, so here your timer object will not respond, You need to create background task handler, check comment of below link,
How to run the timer in background of the application?
In background if you want to continue location service than you need to set pausesLocationUpdatesAutomatically flag, flag info on
pausesLocationUpdatesAutomatically flag info
if ([self.locationManager respondsToSelector:#selector(pausesLocationUpdatesAutomatically)]) {
self.locationManager.pausesLocationUpdatesAutomatically = NO;
}
check my comment on
Location service going to "Inactive" state in iPhone 5
For location manager there below are the CLLocationManagerDelegate methods for location update,
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation [Deprecated in __IPHONE_6_0]
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_6_0);
Where you found
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation ??
Use below code in your didEnterRegion Method to start updates again
[_locationManager startMonitoringSignificantLocationChanges];
if ([_locationManager respondsToSelector:#selector(setAllowsBackgroundLocationUpdates:)])
{
_locationManager.allowsBackgroundLocationUpdates =YES;
}
[_locationManager startUpdatingLocation];