4

I make a MapViewController adopt the CLLocationManagerDelegate protocol this way:

extension MapViewController:CLLocationManagerDelegate{

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

        let usrLocation:CLLocation = locations[0] as CLLocation

        // readable
        print("lat = \(usrLocation.coordinate.latitude)")
        print("lon = \(usrLocation.coordinate.longitude)")

        self.userLocation = usrLocation

        locationManager.stopUpdatingLocation()

    }

    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error)
    {
        print("Error \(error)")
    }

    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {

        switch status {
        case .restricted, .denied, .notDetermined:

            // no issue here because I make a very generic request to a server
            // AND I don't need user coordinates

            break

        case .authorizedWhenInUse, .authorizedAlways:

            // here I need user coordinates because I need to request content to a server
            // based on user coordinates but when I check for userLocation
            // at first I find <+0.00000000,+0.00000000> +/- 0.00m (speed -1.00 mps / course -1.00

            print("----------------")
            print(userLocation)
            print("----------------")

            break
        }
    }

I need user location data in the body of the .authorizedWhenInUse/.autorizeAlways case because I need to send current user coordinates to a server. Problem is when I need them I get at first a user location like this one:

 <+0.00000000,+0.00000000> +/- 0.00m (speed -1.00 mps / course -1.00>

userLocation is member of the viewcontroller class and its value is set in locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]). Should I move the request to this function? And how do I check the user status in that context? I only want to make requests for users who authorized the app to retrieve their position. I hope the solution is not a timer and multiple checks, but any help is appreciated :-)

Gustavo Vollbrecht
  • 3,188
  • 2
  • 19
  • 37
  • 2
    Completely unrelated, in Swift, you don’t need or want `break` statements in your `case` statements inside a `switch`. – Rob Jan 21 '19 at 18:10
  • In this case `.authorizedWhenInUse, .authorizedAlways:` call `manager.requestLocation()` to get the current location immediately. – Kamran Jan 21 '19 at 18:13
  • @Rob: I knew the same, but when I found this in the official docs, I thought something had changed: https://developer.apple.com/documentation/corelocation/choosing_the_authorization_level_for_location_services/requesting_when-in-use_authorization –  Jan 22 '19 at 06:06
  • Nope, nothing changed here. Just bad code snippet on Apple’s part. Only the empty `case` statement needs `break`... – Rob Jan 22 '19 at 06:22
  • @Rob: ok, no problem –  Jan 22 '19 at 06:23
  • @Kamran: it has no effect, thanks anyway :-) –  Jan 22 '19 at 06:49

1 Answers1

3

Just because you’ve received .authorizedWhenInUse or .authorizedAlways doesn’t mean that you’ve started location services yet. It just means that it has been authorized. You shouldn’t try to retrieve the user location in didChangeAuthorization.

So, you should call startUpdatingLocation and move your code that sends this location to the server to your didUpdateLocations method.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Rob, I couldn't post all the code: of course, in the rest of the code I start location services. Moreover, didChangeAuthorization is called anytime my viewController is loaded. Problem with your suggestion is the authorization status: the authorized user must make a request, while other users must make another one. If I put the request inside the "generic" didUpdateLocations I must check user status from there (how?) and conditionally make my requests –  Jan 22 '19 at 06:11
  • In your generic routine, you’d just have a test that says “hey, did I already send that initial location request”, and act accordingly. Keep track of this through some ivar. Or have one `CLLocationManagerDelegate` for this startup process and another for the ongoing requests. Lots of ways of doing it. But yes, `didUpdateLocations` is where you get the location. – Rob Jan 22 '19 at 06:25
  • At that point in the code I just know I sent a location request because anything is done long before, in the viewDidLoad body. So, I thought I should check the authorizationStatus in didUpdateLocation, but this would lead me to repeat the switch I have in didChangeAuthorization, that's not exactly a DRY solution :-) Maybe the ivar could help –  Jan 22 '19 at 06:42
  • maybe mine is a false issue: that is, PLEASE correct me and if I'm wrong, the unauthorized user NEVER reaches the didUpdateLocations, just because he can't by design. This means ONLY the authorized user can reach it. So, my non-generic request should live in didUpdateLocations, while the generic one should be elsewhere. Don't need for any computed property to retrieve the status of the user –  Jan 22 '19 at 07:33
  • 1
    Yes, if authorization hasn’t been granted, it obviously won’t call `didUpdateLocations` with locations... – Rob Jan 22 '19 at 07:38
  • even if this can sound strange, to me it wasn't that obvious. Thanks for helping :-) –  Jan 22 '19 at 07:39
  • don't worry: I focused too much on the authorizationStatus and I forgot for a moment "the big picture", thanks again :-) –  Jan 22 '19 at 08:03