Andrea Giusti
Andrea Giusti

Reputation: 455

Swift requestWhenInUseAuthorization not working

I'm writing an app for iOS 9.3 using swift and need a map to show user location. I initialize CLLocationManager and its delegate, I configure info.plist Privacy - Location Usage Description string and invoke requestWhenInUseAuthorization but it does not show any authorization request.

Here is my code:

import UIKit
import MapKit

class DetailController: UIViewController, CLLocationManagerDelegate {

    var locManager: CLLocationManager!

    @IBOutlet var myMap: MKMapView!

    override func viewDidLoad() {
        super.viewDidLoad()

        mostraPosizione()
    }

    func mostraPosizione(){
        locManager = CLLocationManager()
        locManager.delegate = self
        locManager.desiredAccuracy = kCLLocationAccuracyBest

        let authorizationStatus = CLLocationManager.authorizationStatus()

        if (authorizationStatus == CLAuthorizationStatus.NotDetermined) {
            locManager.requestWhenInUseAuthorization()
        } else {
            locManager.startUpdatingLocation()
        }


        myMap.showsUserLocation = true
    }

    func locManager(manager: CLLocationManager, 
    didChangeAuthorizationStatus status: CLAuthorizationStatus) {

        if (status == CLAuthorizationStatus.NotDetermined) {
            locManager.requestWhenInUseAuthorization()

        } else {
            locManager.startUpdatingLocation()
        }
    }
}

and a screenshot from my info.plist info.plist

Xcode output:

Trying to start MapKit location updates without prompting for location authorization. Must call -[CLLocationManager requestWhenInUseAuthorization] or -[CLLocationManager requestAlwaysAuthorization] first.

I'm testing it on iPhone 5 s (iOS 9.3) simulator.

What's the problem with this stuff? Does anyone have any advice?

Upvotes: 16

Views: 14350

Answers (4)

Murray Sagal
Murray Sagal

Reputation: 8654

I am targeting iOS 14. I'm only doing When In Use.

I had to have all three strings:

enter image description here

But that wasn't enough. elight's comment (Jan 25, 19) about separating the call to CLLocationManager from the request turned out to be required as well. I had to declare a property, initialize it in viewDidLoad() and then call requestWhenInUseAuthorization() later.

And finally, when testing, deleting the app wasn't enough. I saw a comment on another SO answer where that dev had to open the Settings app after deleting their app to test again. It turns out that is required in my case too.

Seriously, I'm not pulling your leg.

Upvotes: 1

Marlon Mingo
Marlon Mingo

Reputation: 205

Try this on for size:

override func viewDidLoad() {
    super.viewDidLoad()

    //put code here
    locManager = CLLocationManager()
    locManager.delegate = self
    locManager.desiredAccuracy = kCLLocationAccuracyBest
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    //make sure the view is loaded first before adding subviews
    mostraPosizione()
}

func mostraPosizione(){
    let authorizationStatus = CLLocationManager.authorizationStatus()

    if (authorizationStatus == .authorizedWhenInUse) {
        locManager.startUpdatingLocation()
        myMap.showsUserLocation = true
    } else {
        locManager.requestWhenInUseAuthorization()
        mostraPosizione()
    }
}

The reason the request was not showing up was because the view was not completed loaded when the request was called. Using viewDidAppear() fixes that issue.

I made some changes to the code as well, which seems to work great. I only made changes to the code below, other functions remained the same.

I'm writing this code in Swift 3, but the principals are the same.

Upvotes: 5

Andrea Giusti
Andrea Giusti

Reputation: 455

Add NSLocationWhenInUseUsageDescription key to info.plist is the solution.

Upvotes: 3

Chajmz
Chajmz

Reputation: 749

I think it's because

let authorizationStatus = CLLocationManager.authorizationStatus()

is nil so your requestWhenInUseAuthorization() is never called.

  let authorizationStatus = CLLocationManager.authorizationStatus()

    if (authorizationStatus == CLAuthorizationStatus.NotDetermined) || (authorizationStatus == nil) {
        locManager.requestWhenInUseAuthorization()
    } else {
        locManager.startUpdatingLocation()
    }

should be working.

Edit:

You should also handle the case if the user decline the request

if (authorizationStatus == CLAuthorizationStatus.Denied ) {
  // Display a message, do what you want 
}

Indeed once the user has declined the authorization you can't make the popup appears again, users have to go in the app settings and set by themselves the authorization to use their location. However you can now have a link directly from your app to the settings, so it's easier for users.

// Open the settings of your app
if let url = NSURL(string:UIApplicationOpenSettingsURLString) {
    UIApplication.sharedApplication().openURL(url)
}

Upvotes: 14

Related Questions