Search code examples
iosswiftcore-locationcllocation

NSLocation doesn't wait for me to click allow


When I run the code, the window pops asking for permission to use location but disappears almost immediately, not giving the user a chance to click "Allow". Is there a way to force this action before proceeding?

import UIKit
import MapKit
import CoreLocation

class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate{

var map:MKMapView?
var manager:CLLocationManager!

convenience init(frame:CGRect){
    self.init(nibName: nil, bundle: nil)
    self.view.frame = frame

    self.map = MKMapView(frame: frame)
    self.map!.delegate = self

    self.view.addSubview(self.map!)

}

override func viewDidLoad() {
    super.viewDidLoad()

    // Core Location
    manager = CLLocationManager()
    manager.delegate = self
    manager.desiredAccuracy = kCLLocationAccuracyBest

}

func locationManager(manager: CLLocationManager!,
    didChangeAuthorizationStatus status: CLAuthorizationStatus){

        print("The authorization status of location services is changed to: ")

        switch CLLocationManager.authorizationStatus(){
        case .Denied:
            println("Denied")
        case .NotDetermined:
            println("Not determined")
        case .Restricted:
            println("Restricted")
        default:
            println("Authorized")
        }

}

func displayAlertWithTitle(title: String, message: String){
    let controller = UIAlertController(title: title,
        message: message,
        preferredStyle: .Alert)

    controller.addAction(UIAlertAction(title: "OK",
        style: .Default,
        handler: nil))

    presentViewController(controller, animated: true, completion: nil)

}

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

    if CLLocationManager.locationServicesEnabled(){
        switch CLLocationManager.authorizationStatus(){
        case .Denied:
            displayAlertWithTitle("Not Determined",
                message: "Location services are not allowed for this app")
        case .NotDetermined:
                manager.requestWhenInUseAuthorization()
                manager.startUpdatingLocation()
        case .Restricted:
            displayAlertWithTitle("Restricted",
                message: "Location services are not allowed for this app")
        default:
            println("Default")
        }

    } else {
        println("Location services are not enabled")
    }

}


func locationManager(manager:CLLocationManager, didUpdateLocations locations:[AnyObject]) {

    var userLocation:CLLocation = locations[0] as! CLLocation
    var latitude:CLLocationDegrees = userLocation.coordinate.latitude
    var longitude:CLLocationDegrees = userLocation.coordinate.longitude
    var latDelta:CLLocationDegrees = 1.0
    var lonDelta:CLLocationDegrees = 1.0

    var span:MKCoordinateSpan = MKCoordinateSpanMake(latDelta, lonDelta)
    var location:CLLocationCoordinate2D = CLLocationCoordinate2DMake(latitude, longitude)
    var region:MKCoordinateRegion = MKCoordinateRegionMake(location, span)
    map!.setRegion(region, animated: true)
    manager.stopUpdatingLocation()

}

Solution

  • It's because you're calling manager.startUpdatingLocation() before you get the result from the manager.requestWhenInUseAuthorization(). Even though you call requestWhenInUseAuthorization, you're updating the user's location before you ever get the result of that method (I had the exact same question as you, actually!)

    The answer to that question explains the solution well. Basically, you'll need to implement the locationManager:didChangeAuthorizationStatus delegate method, which is called any time the authorization status changes based on user input. If the user did authorize tracking, then you can call manager.startUpdatingLocation().

    Also, for a Swift example of how to implement these methods, take look at this guide.