scttcrry scttcrry -4 years ago 99
Swift Question

Swift - Location Prompt not happening soon enough

I am building a location-based app that lists nearby coffee houses. App keeps crashing on first build on device because location keeps returning as nil.

This is because the Privacy - Location prompt isn't happening soon enough, even though though the request is earlier in the code. After I close the app after it crashes, that's when I'm prompted to allow my location.

I have three onboarding screens, and when I get to this tableviewcontroller, that's when it crashes.

If I go into Settings > Privacy > Location and manually enable location services, the app works great.

Here's my code (I removed a ton of unnecessary stuff):

import UIKit
import MapKit
import CoreLocation

class ShopTableViewController: UITableViewController, CLLocationManagerDelegate {




@IBAction func filterBack(_ sender: Any) {
getLocale()
shops.sort() { $0.distance < $1.distance }
shops.removeAll()
loadShops()
sortList()

}


//MARK: Properties

var shops = [CoffeeShop]()
var filteredShops = [CoffeeShop]()
var objects: [CoffeeShop] = []
var locationManager = CLLocationManager()
func checkLocationAuthorizationStatus() {
if CLLocationManager.authorizationStatus() == .authorizedWhenInUse {
locationManager.requestWhenInUseAuthorization()
}
}
var currentLocation = CLLocation!.self
var userLatitude:CLLocationDegrees! = 0
var userLongitude:CLLocationDegrees! = 0
var locValue:CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 1.0, longitude: 1.0)
var refresher: UIRefreshControl! = UIRefreshControl()





func getLocale() {

self.locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
self.locationManager.startMonitoringSignificantLocationChanges()

userLatitude = self.locationManager.location?.coordinate.latitude
userLongitude = self.locationManager.location?.coordinate.longitude
print("\(userLatitude), \(userLongitude)")
}



override func viewDidLoad() {
super.viewDidLoad()

self.locationManager = CLLocationManager()
/// self.locationManager.requestWhenInUseAuthorization()
checkLocationAuthorizationStatus()

self.locationManager.delegate = self
self.locationManager.startUpdatingLocation()
if CLLocationManager.locationServicesEnabled()
{
getLocale()

}

let locValue = self.locationManager.location?.coordinate
noHeight()
loadShops()
sortList()
print("\(locValue?.latitude), \(locValue?.longitude)")

refresher = UIRefreshControl()
refresher.addTarget(self, action: #selector(ShopTableViewController.handleRefresh), for: UIControlEvents.valueChanged)


if #available(iOS 10, *) {
shopTable.refreshControl = refresher
} else {
shopTable.addSubview(refresher)
}


}
}


What am I doing wrong?

Answer Source

requestWhenInUseAuthorization() is an asynchronous method, so your method that wraps it checkLocationAuthorizationStatus() is also async.

However, in your viewDidLoad, you call

checkLocationAuthorizationStatus()

self.locationManager.delegate = self
self.locationManager.startUpdatingLocation()

This is triggering the locationManager to start before it is authorized. Take a look here at this (somewhat old) link http://nshipster.com/core-location-in-ios-8/

Example

Be sure to conform to CLLocationManagerDelegate

override func viewDidLoad() {
    super.viewDidLoad()
    self.locationManager = CLLocationManager()
    self.locationManager.delegate = self
    if CLLocationManager.authorizationStatus() == .notDetermined {
        locationManager.requestWhenInUseAuthorization()
    } else if CLLocationManager.authorizationStatus() == . authorizedWhenInUse {
        startTrackingLocation()
    }
}

func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
    if status == .authorizedAlways || status == .authorizedWhenInUse {
        startTrackingLocation()
        // ...
    }
}

func startTrackingLocation() {
    locationManager.startUpdatingLocation()
    getLocale()
    //not clear which of these methods require location
    let locValue = self.locationManager.location?.coordinate
    noHeight()
    loadShops()
    sortList()
    print("\(locValue?.latitude), \(locValue?.longitude)")
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download