I am building an app for iOS that needs to know when the user is in a range of 20-30 meters of their home.
I user geofence to trigger an event when the user is 500m from their home, then I use CLLocationManager to start tracking the actual location and check distance to their home.
My problem is that when I call "startUpdatingLocation" from my geofence event while the app is in the background, it only runs for like 20 seconds and then stops giving any callbacks.
If I, after the 20 seconds have passed open the app and puts it back to the background then it keeps giving me callbacks until I stop it.
If I call "startUpdatingLocation" while the app is running and then puts it into the background, it keeps running as it should.
My code for initialisation looks like this:
init(config: AutounlockConfiguration) {
super.init()
self.config = config
self.locationManager = CLLocationManager()
self.locationManager.requestAlwaysAuthorization()
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.delegate = self
self.locationManager.pausesLocationUpdatesAutomatically = false
self.locationManager.allowsBackgroundLocationUpdates = true
self.locationManager.activityType = .automotiveNavigation
self.locationManager.distanceFilter = kCLDistanceFilterNone
self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
}
and when I start updating location I do it like this:
func startFetchingUserLocation() {
if (!isFetchingLocation) {
isFetchingLocation = true
DanaDebugger.presentDebugPushMessage(message: "fetching new location")
locationManager.startUpdatingLocation()
}
}
I have enabled the background mode for location, and included the NSLocationAlwaysUsageDescription in my info.plist file.
I really have no clue where to go from here, hope somebody is able to help.
locationManager.startMonitoringSignificantLocationChanges()
Use this line instead of this line
locationManager.startUpdatingLocation()
it will solve your problem
Related
I am working on concept of guard application where in i need guard actual location every 60 seconds. same functionality was working in iOS 12, but in iOS 13 and 14 not working as it should be. i have made below changes for location manager.
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.distanceFilter = 2
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
locationManager.activityType = .other
locationManager.allowsBackgroundLocationUpdates = true
locationManager.pausesLocationUpdatesAutomatically = false
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
locationManager.startMonitoringSignificantLocationChanges()
am getting precise location when app is in foreground, but as soon as app goes background am not getting actual location. it would be great if any one can help me out as its causing the productivity of app and its basic usage.
Request the requestAlwaysAuthorization, if you are going to use the user’s location in the background.
let authorizationStatus = CLLocationManager.authorizationStatus()
if authorizationStatus == .authorizedAlways {
locationManager.startMonitoringSignificantLocationChanges()
} else {
locationManager.requestAlwaysAuthorization()
}
And also update the Info.plist:
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
Please, make sure to read the documentation first.
Apart from setting the allowsBackgroundLocationUpdates to true, you also need to check the “Location updates” capability for your app in Xcode.
When a location update occurs while your app is in the background, the system launches your app and passes an options dictionary to the application(:willFinishLaunchingWithOptions:) or application(:didFinishLaunchingWithOptions:) methods. The dictionary may contain the location key to indicate that location services caused the app to launch.
I am trying to create a simple iOS app that will continuously track location in the background and notify with high accuracy when the user has entered a specific region (don't want to use region monitoring because it's not accurate or fast enough for what I want).
This app works fine in the foreground but once I go into the background it does not work.
I created a test app to investigate how background location updates work. I made a simple app that just prints out a message when a location update happens (to the log).
What I see is that in foreground mode the updates happen as expected but when I lock my phone and the app switches to background the location updates happen for about 30 seconds and then stop. There is no mention in the Apple docs (that I can find) explaining this behavior.
I made sure that I enabled background processing in the info.plist (done in "capabilities" --> "background modes" --> "location updates")
Here is the code I used to test this:
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
let locationManager = CLLocationManager()
var counter = 0
override func viewDidLoad() {
super.viewDidLoad()
// configure location updates
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = 0.1
locationManager.pausesLocationUpdatesAutomatically = false
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("location updated! \(counter)")
counter = counter + 1
}
Any ideas on what I might be missing? I tried different location accuracy settings (such as BestForNavigation, etc) and no change.
Thanks.
You have to add this in your plist :
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
add this
locationManager.allowsBackgroundLocationUpdates = true
I think this might be the answer:
allowsBackgroundLocationUpdates.
Apps that want to receive location updates when suspended must include
the UIBackgroundModes key (with the location value) in their app’s
Info.plist file and set the value of this property to true.
...
The
default value of this property is false.
Are you setting that?
You can add these two line to make it working in background mode:
locationManager!.allowsBackgroundLocationUpdates = true
locationManager!.pausesLocationUpdatesAutomatically = false
In an iPhone app that I'm building, GPS accuracy is a user-controlled value that can be set to either KCLLocationAccuracyNearestTenMeters or KCLLocationAccuracyHundred Meters. The code works fine to capture a track, but when monitoring the app in the Location Energy Impact Instrument I’m seeing some unexpected behavior. When accuracy is set to nearest 10 meters the app works as expected, however, if it’s set to 100 meters the setting remains OK for between a half and two seconds, as shown by the Instrument, but then it switches to KCLLocationAccuracy Best. This setting isn't an option in the app, or even a String anywhere in the code.
One twist here, I am running this as Swift 3.0 using Xcode 8, but it's working as it did before and the new platform doesn't seem to be an issue (and shouldn't have this kind of impact). It does mean that some API's have changed and may look different (and to my taste, better) below.
Obviously, this resetting has a very counterproductive impact on energy consumption, which shows in the Instrument which goes from Low to High energy usage at the point where it switches. I can't identify any point where the app would be doing this unintentionally - in fact, during this one-second period it should only be appending points returned by didUpdateLocations to a pending buffer for later processing, as shown below.
In a shared constants declaration:
let defaultTrackingAccuracy = kCLLocationAccuracyNearestTenMeters
let alternateTrackingAccuracy = kCLLocationAccuracyHundredMeters
var trackingAccuracy = kCLLocationAccuracyNearestTenMeters
var waypointInterval = 100
In a settings ViewController (can also reset waypointInterval but did not):
#IBAction func accuracySwitched(_ sender: UISwitch) {
if accuracySwitch.isOn {
Set.shared.trackingAccuracy = Set.shared.defaultTrackingAccuracy
} else {
Set.shared.trackingAccuracy = Set.shared.alternateTrackingAccuracy
}
}
LocationManager instantiation in a Model portion of code:
lazy var locationManager: CLLocationManager = {
var _locationManager = CLLocationManager()
_locationManager.delegate = self
_locationManager.desiredAccuracy = Set.shared.trackingAccuracy
_locationManager.allowsBackgroundLocationUpdates = true
_locationManager.distanceFilter = Double(Set.shared.waypointInterval)
return _locationManager
}()
Starting the location manager updates in the Model:
func setupLocationManager()
{
if CLLocationManager.authorizationStatus() != .authorizedAlways {
locationManager.requestAlwaysAuthorization()
if CLLocationManager.authorizationStatus() != .authorizedAlways {
delegate?.displayNotice("Unable to Capture Track", alertMessage: "This device requires authorization to use location services in order to capture a track. \n\nPress Continue to return to the Track List.", buttonText: "Continue")
}
}
if !CLLocationManager.locationServicesEnabled() {
delegate?.abortTrackCapture(.gpsUnavailable)
}
locationManager.startUpdatingLocation()
}
Processing points returned by the location manager instance, in the same Model portion:
#objc(locationManager:didUpdateLocations:) func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
assert(locations.first != nil, "didUpdateLocations called with empty array.")
if recordStatus == .record {
pendingPointsBuffer = pendingPointsBuffer + locations
}
lastPosition.latitude = locations.last!.coordinate.latitude
lastPosition.longitude = locations.last!.coordinate.longitude
lastPosition.elevation = locations.last!.altitude
}
Every few seconds, there is a routine that takes points from the buffer and posts them into a CoreData database, but that only accesses the buffer - it has no interaction with the location manager (so it can sleep through periods when the app is in background while the code above keeps loading points into the buffer).
The user interface is in a separate ViewController module, but shouldn’t be doing anything during the two seconds in question.
I'm thinking that there may be some OS based setting or process that could be doing this, but I haven't found anything that seems to do that.
Thanks for your input - any thoughts are appreciated.
If you have read the new iOS 9 docs, you may have noticed a new method which causes the location manager to power up radios for a brief amount of time, get a fix on your location and once desired accuracy (or timeout) is reached, turn it all back off, handing the pinpointed location over to the delegate.
The method is called CLLocationManager.requestLocation() and is available in the iOS 9 SDK. Alas, I'm currently working on an app targeting iOS 8 and would still very much like to make use of this method. I also wouldn't like to reimplement it all myself.
So here's my question: Is there any open-source library for iOS implementing this kind of one-time location retrieval?
You need to perform two steps first
1) NSLocationWhenInUseUsageDescription
2) NSLocationAlwaysUsageDescription
Then
var locationManager = CLLocationManager()
locationManager.delegate = self
// locationManager.locationServicesEnabled
locationManager.desiredAccuracy = kCLLocationAccuracyBest
let Device = UIDevice.currentDevice()
let iosVersion = NSString(string: Device.systemVersion).doubleValue
let iOS8 = iosVersion >= 8
if iOS8{
locationManager.requestWhenInUseAuthorization()
}
else{
locationManager.requestAlwaysAuthorization()
}
locationManager.startUpdatingLocation()
This will Help.Thanksyou
As Arslan Asim pointed out, a good solution would be to use INTULocationManager.
From the docs:
manager.requestLocationWithDesiredAccuracy(.Block, timeout: 10.0) {
(currentLocation, achievedAccuracy, status) in
// Examine status to see if the request timed out.
// If not, currentLocation stores the found location.
}
Here is my code from a ViewController implementing CLLocationManagerDelegate:
func startLocationManager() {
let locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
println("I'm called")
locationManager.requestAlwaysAuthorization()
// locationManager.startMonitoringSignificantLocationChanges()
locationManager.startUpdatingLocation()
let status = CLLocationManager.authorizationStatus()
println(status.rawValue) // This print 0 which stands for kCLAuthorizationStatusNotDetermined
println(CLLocationManager.locationServicesEnabled()) // true
}
func locationManager(manager: CLLocationManager!,
didUpdateLocations locations: [AnyObject]!) {
println("nobody call me ever, and I'm sad")
}
For some reason, I never get the prompt / alter to autorise location updates. I have tried on my device iOS 8.1 and the simulartor. I followed the advices found here: requestAlwaysAuthorization not showing permission alert :
"Add Core Location framework to Project Settings / Targets / Capabilities / Background Modes set "Location Updates" and "Uses Bluetooth LE Accessories" Add key at Info.plist NSLocationAlwaysUsageDescription".
I have also tried to clean up and rebuild, nothing change. I feel clueless.
EDIT: This question seems related: iOS: App is not asking user's permission while installing the app. getting kCLAuthorizationStatusNotDetermined every time - Objective-c & Swift but the selected answer and its article doesn't expose anything new
Your CLLocationManager object is local object and thus will be deallocated immediately after it falls out of scope. Make it a class property and then asynchronous processes like requesting authorization and determining the location will have a chance to run.