I do not understand, I set PausesLocationUpdatesAutomatically to true, don't move the phone and after I call startUpdatingLocation the locationmanager never pause :( what did I miss ?
AllowsBackgroundLocationUpdates = true;
PausesLocationUpdatesAutomatically = true;
requestAlwaysAuthorization
startUpdatingLocation
I continuously receive DidUpdateLocations but no DidPauseLocationUpdates
Try to set activityType of your CLLocationManager to automotiveNavigation. Default other is set
How long did you wait? I had a similar case for when my authorizationStatus was set to requestWhenInUseAuthorization I was working for and it took roughly 16-17 minutes for it to pause. During that time even if the phone was sitting on my desk I was usually still getting updates sent to my didUpdateLocation method. If I remember correctly upon putting the app into background I was getting 2 location updates at the first few seconds I placed it the background and then like two updates at minute 1, another two updates at minute 5, another two at minute 10, another two at minute 14 and eventually another two before 17 and then the pause would happen. I guess the app is receiving the callbacks which check to 'make sure you've actually paused'
Though I didn't set the activityType to automotiveNavigation. I'm assuming setting to that will reduce the time it takes to find out it has to fire the locationManagerDidPauseLocationUpdates callback.
Additionally the callback you will get is locationManagerDidPauseLocationUpdates this. You said DidPauseLocationUpdates but I'm guessing you're talking about the same thing...
Related
Trying to get my app to use less power, it's tracking location always in the background but I'd like for it automatically pause so I can turn on region watching and use that to resume precise location monitoring once the user moves around a bit.
I've had the app on for half an hour now and the location service is not pausing. I think this has been the case since Apple changed location stuff in iOS 13? I'm not really sure. All the documentation I can find online seems extremely outdated.
Any advice is greatly appreciated, relevant code follows:
init() {
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = 10
locationManager.activityType = .fitness
locationManager.requestAlwaysAuthorization()
locationManager.allowsBackgroundLocationUpdates = true
locationManager.pausesLocationUpdatesAutomatically = true
locationManager.showsBackgroundLocationIndicator = true
locationManager.startUpdatingLocation()
}
func locationManagerDidPauseLocationUpdates(_ manager: CLLocationManager) {
delegate?.paused(tracker: self)
print("MT | LOCATION SERVICES PAUSED!") <---- NEVER GETTING CALLED (been running for 40 minutes now, no location updates, still going though?)
// if not already, start region monitoring
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// Collect data
for location in locations {
print("MT | New Location: \(location)")
}
}
I wouldn't rely on connecting to Xcode. Xcode is generally greedy. It doesn't want you to stop your debugging. So it never puts the app in a suspended state.
The callback of locationManagerDidPauseLocationUpdates is somewhat identical to your app being suspended which Xcode prevents.
The way I would test this is to use os.log. Then log the outputs to your Mac's console app. See here.
Just make sure the app is not launched from you running from Xcode. I'd rather disconnect your device from Xcode, kill the app. Disconnect your device from Xcode. Then just tap on the app icon again. That way Xcode cannot intervene.
The issue is caused by your desired accuracy.
You are setting the accuracy to the best, which will make your application run continuously, so it will never pause in any iOS versions.
Furthermore, in a practical test whether it is pausing or not, change the accuracy to kCLLocationAccuracyHundredMeters. It will definitely pause. You can change the filter accuracy as you need for the next update.
Do you think maybe your activityType is preventing the location services to pause?
activityType
The location manager uses the information in this property as a cue to determine when location updates may be automatically paused. Pausing updates gives the system the opportunity to save power in situations where the user's location is not likely to be changing.
.fitness
This activity might cause location updates to be paused only when the user does not move a significant distance over a period of time.
I don't know what your particular use case is but, have you considered using the following function for background location tracking?
startMonitoringSignificantLocationChanges()
You may also try a mix of both, turning on significant location changes for day to day use and allowsBackgroundLocation when doing something like tracking a run, etc.
I think it is quite clear from the docs that this the pause functionality does not work like that. First of all, there is no guarantee that the GPS hardware will pause at all, it depends on the type of activity as well as other activities in the system. It is up to iOS to decide when and if to pause location updates, the property is just a hint.
More important, your use case would not work since if it gets paused, the user would have to interact with your app manually to resume updates.
Apple mentions your case specifically and recommends reducing the accuracy of location updates so it is only using cell tower triangulation instead of GPS (see docs above).
So, instead of using pausesLocationUpdatesAutomatically = true, you could do desiredAccuracy = kCLLocationAccuracyThreeKilometers.
To quote Apple,
[...] consider disabling this property and changing location accuracy to kCLLocationAccuracyThreeKilometers when your app moves to the background. Doing so allows you to continue receiving location updates in a power-friendly manner
Doesn't this cover your use case?
CLLocationManager.requestLocation() takes around 10 seconds to fire didUpdateLocations event.
Here are the attributes set for the CLLocationManager
let locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = 10
locationManager.requestWhenInUseAuthorization()
locationManager.requestLocation()
As per the documentation this can take several seconds.
This method returns immediately. Calling it causes the location manager to obtain a location fix (which may take several seconds) and call the delegate’s locationManager(_:didUpdateLocations:) method with the result.
But can this take 10 long seconds? Or am I missing something?
If you switch out the
locationManager.requestLocation()
for
locationManager.startUpdatingLocation()
then didUpdateLocations will start firing immediately. This should solve your problem.
TL;DR
requestLocation() is a convenient method provided by Apple which under the hood will run startUpdatingLocation() , retrieve multiple location data, and select the most accurate one to pass to delegate, and call stopUpdatingLocation()
This process can take up to 10 seconds (which is around the timeout limit) if it can't decide which location data is the best.
I think you have a false premise. That Google is always faster. I'm guessing that your building app from scratch and the app has no access to cache. Otherwise GoogleMaps can also sometimes take more than 3 seconds. Obviously I don't know the exact specifics but I just think when you're using GoogleMaps you're using it as a user and now when you're developing your own app you're thinking about it as a developer ie you're being more meticulous about it.
Also to have the best of comparisons make sure you set your desiredAccuracy to BestForNavigation, distanceFilter to 0 and activityType to .automotive. That's normally what navigation apps are doing.
Leo's comment is also important: Make sure you update the UI from the main queue
And as mentioned by both highly experienced in Core-Location users: programmer and Paulw11:
When you call startUpdatingLocation on the location manager you must give it time to get a position. You should not immediately call stopUpdatingLocation. We let it run for a maximum of 10 seconds or until we get a non-cached high accuracy location.
I changed .desiredAccuracy to kCLLocationAccuracyKilometer and my .requestLocation() now returns the position immediately
I found the following line in my Console logs:
Ignoring requestLocation due to ongoing location.
No idea what's going on. My app doesn't call startUpdatingLocation() at all. But when I keep the app running I can see that locationManager(_:didUpdateLocations:) is called periodically.
Once I put stopUpdatingLocation() in front of the requestLocation() it's as fast as expected:
locationManager.stopUpdatingLocation()
locationManager.requestLocation()
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!
I'm making an app that receives constant updates (potentially hundreds of times a day) and, to make for a better user experience, it would be nice to have these downloaded in the background.
Looking at Apple's[1] documentation I need to set the background mode to "Background fetch". Exploring deeper you can read about the application:performFetchWithCompletionHandler[2] function which states that:
When this method is called, your app has up to 30 seconds of wall-clock time to perform the download operation and call the specified completion handler block... If your app takes a long time to call the completion handler, it may be given fewer future opportunities to fetch data in the future.
The problem is our downloads will take longer than 30 seconds to download, and as such would rather not face the wrath of Apple sending updates fewer and farther between, thus exacerbating the issue!
So, am I able to do this somehow?
Also, I have created a crude experiment whereby I create a NSTimer:scheduledTimerWithTimeInterval to run every minute which logs to the console. This successfully works both on the iPhone in simulation (has been running for 30 mins plus) and also when I place it on a phone (a week plus)... why would this be!?
It may be hard to do because of the Apple 30s obligation. They decided so to eventually prevent big download to happen not to drain battery and data plan.
You must be sure you really need to download that much data (as it takes this long) in background, plus hundred times a day!
I mean, when your app goes foreground after a (long) background period, it may not be updated and it's normal case. So you need to do the update when the app goes foreground; only one update is needed. Otherwise, you should step back and rethink the update process.
Found a solution:
You can bypass Apple's application:performFetchWithCompletionHandler function and set your own timer. To do this make sure you do the following:
Have selected Background fetch under Your app settings > "Capabilities" > "Background Modes".
In AppDelegate.m, within application:didFinishLaunchingWithOptions, add the following code:
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
// Nothing
}];
You can now add a NSTimer in your AppDelegate and it will continue to run whilst in the background. E.g.,
_timerBg = [NSTimer scheduledTimerWithTimeInterval:1800
target:self
selector:#selector(bgFunction)
userInfo:nil
repeats:YES];
I've got an app that requires continuous GPS updates even when in the background. I've called startUpdatingLocation and have "App registers for location updates" set and I get fixes for about 15 minutes and then the application just stops getting updates.
What am I missing here?
Did you try to set :
myLocationManager.pausesLocationUpdatesAutomatically = NO;
The default value for this property is YES.
This helped for me, I had your same problem.
I think I figured it out. On iOS6 in order to save power you stop getting location updates because you are not moving.
locationManagerDidPauseLocationUpdates fires when this occurs.
Did you set the locationManager’s delegate? It was suggested here.
Also: perhaps it is dropping down to a lower accuracy after 15 minute, and giving you notifications of only large changes. Go for a drive, or set distant locations in the simulator, and see if you get updates then.