Unable to get the permission prompt from CLLocationManager - ios

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.

Related

What's the proper way to use startMonitoringSignificantLocationChanges in swift 4?

I try to make a running app, and I want to use the method startMonitoringSignificantLocationChanges, but I'm missing something because is not firing the didUpdateLocations that the documentation says it should.
I have successfully used the startUpdatingLocation method of the CLLocationManager class and I can totally can use it for my interests, but I'd like to understand why my implementation doesn't work.
So, I have this code:
let locationManager = CLLocationManager()
func determineMyCurrentLocation() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager.allowsBackgroundLocationUpdates = true
print("authorization Status: \(CLLocationManager.authorizationStatus().rawValue)")
if CLLocationManager.locationServicesEnabled() {
locationManager.startUpdatingLocation() //(1)
//(2)locationManager.startMonitoringSignificantLocationChanges()
locationManager.distanceFilter = 20
//locationManager.start
//locationManager.startUpdatingHeading)(
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { print("yaaay!") }
If you comment (1) and uncomment 2 the func locationManager...etc is never fired.
Also, I have my info.plist with the proper keys set, "Privacy - Location When In Use Usage Description" and "Privacy - Location Always and When In Use Usage Description").
Any idea?
Thanks in advance.
EDITED: My question was imprecise. I put the locationManager property as a class member and I completed the properties in my info.plist
The startMonitoringSignificantLocationChanges requires always authorisation. That also means you should turn on "Background Modes" in Capabilities section and selection "Location Updates". You also require the Privacy - Location Always Usage Description Try these two and you should receive significant location change updates.

continuous iOS location updates in background

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

Getting current location using CLLocationManager in Swift

How do I get the current location of my iOS device?
It seems that Apple has made changes that to how get location and I could not find an up-to-date post. I answered this question myself below:
I couldn't find an up-to-date solution to getting the current location so here's how I did it.
My source is apple's sample code on location tracking and smart watch called PotLocCoreLocationwithiPhoneandAppleWatch: https://github.com/robovm/apple-ios-samples/tree/master/PotLocCoreLocationwithiPhoneandAppleWatch
Here's what you have to do in your app.
Note that everything from steps 2-6 are in this gist: https://gist.github.com/JonMercer/75b3f45cda16ee5d1e0955b2492f66dd
In the project settings in xcode, click on the capabilities tab (it's the tab beside the general tab where you put in your bundle identifier). Then turn on Background Mode and enable Location updates. EDIT thanks to #Rob: And also add NSLocationWhenInUseUsageDescription to your plist. The value can be anything you like
Inside your view controller, extend the CLLocationManagerDelegate
class YourClass: UIViewController, CLLocationManagerDelegate {
Implement these two functions
/**
Increases that location count by the number of locations received by the
manager. Updates the batch count with the added locations.
*/
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
//use the "locations" variable here
}
/// Log any errors to the console.
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print("Error occured: \(error.localizedDescription).")
}
Create a let manager = CLLocationManager() variable
On viewDidLoad() set the following: manager.requestWhenInUseAuthorization(), manager.allowsBackgroundLocationUpdates = true, and manager.startUpdatingLocation()
In order to stop tracking do the following: manager.stopUpdatingLocation() and manager.allowsBackgroundLocationUpdates = false

CLLocationManager didUpdateLocations not being called iOS 8 with plist entries

I'm having some issues with CLLocationManager. This code used to work on iOS 8.2 but since upgrading to 8.3 it doesn't work. Here is the code for setting up the location manager which is called on startup.
let distanceThreshold:CLLocationDistance = 100.0
var currentLocation:CLLocationCoordinate2D?
override init() {
assert(locMan == nil)
super.init()
locMan = self
let locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
locationManager.requestAlwaysAuthorization()
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
currentLocation = manager.location.coordinate
if UIApplication.sharedApplication().applicationState == .Background {
PlacesManager.fetchNearbyPlaces(LocationManager.getLocationManager().currentLocation!, radius: distanceThreshold, callback: placesCallback)
}
}
With this code didUpdateLocations is never called despite it being called before.
I have added the relevant entries to the Info.plist file:
I have tried it on both a device and the simulator and neither works. In fact it seems that it is no longer requesting location authorisation anymore if I delete the app and reinstall.
I know I'm missing something stupid but I can't workout what the hell it is.
I'd appreciate any help people can provide.
Cheers,
Gerard
Answer from Anna:
The CLLocationManager documentation says: "To configure and use a CLLocationManager object to deliver events...Create an instance of the CLLocationManager class and store a strong reference to it somewhere in your app. Keeping a strong reference to the location manager object is required until all tasks involving that object are complete. Because most location manager tasks run asynchronously, storing your location manager in a local variable is insufficient."
In addition to the accepted answer, if you are using the simulator. You have to select a location after the app is running (Debug->Location->apple for apple headquarters). I set the location and assumed that the next time I ran the app that didUpdateLocations would be called with what I had set previously, but that assumption was wrong.

CoreLocation Authorization issues - never prompted for authorization

I was testing out CoreLocation to learn how to capture and record the location of the user. I built a simple Master-Detail app which, in the detail pane, showed the user's live location. Everything worked as expected.
Next, I moved on to making my real app which also uses CoreLocation. I built the app with a Master-Detail style and also made it so that when the user opens the detail pane, it should show their current, live, location. However, nothing happens at all.
What I determined after a LOT of debugging and research is that as soon as my locationManager is created and I call locationManager.startUpdatingLocation() the authorization to get the location is changed to denied (or false, or whatever its called). The only way I can get live tracking is build and run the app in Xcode, once it opens in the simulator, open up Location Services and change the app setting to allow location tracking to "Always". Then I can see in the console that locations are being acquired.
I don't understand why I have to keep changing the authorization to "Always" over and over. I have deleted the app on the simulator, done a "clean" from XCode to start fresh, and still it starts on the simulator without authorization.
Anyone have ideas?
UPDATE Now I see that, when I build and it runs in the simulator, I get ONE location, then the authorization is changed to not allow location services.
Here's my code (which worked on another app):
import UIKit
import CoreLocation
class DetailViewController: UIViewController, CLLocationManagerDelegate {
var locationManager : CLLocationManager!
func startTrackingLocation() {
println("tracking engaged")
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager.requestWhenInUseAuthorization() // <-- this was originally commented out
locationManager.startUpdatingLocation()
println("and we're tracking")
println(locationManager.location)
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
println("location acquired") // <--- Only see this when I manually allow location when app is running
self.newLongLabel?.text = "\(locations[0].coordinate.longitude)"
self.newLatLabel?.text = "\(locations[0].coordinate.latitude)"
}
override func viewDidLoad() {
super.viewDidLoad()
self.startTrackingLocation()
self.configureView()
}
// more class stuff...
If you're certain you have added the key to plist file try checking for it in the didChangeAuthorizationStatus: location manager delegate method.
Be sure you have both keys in plist as type string and with the values you would like to display to the user:
NSLocationAlwaysUsageDescription
NSLocationWhenInUseUsageDescription
- (void) locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status{
if (status == kCLAuthorizationStatusAuthorizedWhenInUse || status == kCLAuthorizationStatusAuthorizedAlways){
if ([CLLocationManager locationServicesEnabled]) {
[yourLocationManager startMonitoringSignificantLocationChanges];
//run your code here
}
}
}

Resources