So I want to collect user location when a user tap's a button, and use that location later on in my app. With my current implementation, when the button is tapped, the user should be prompted to allow location to be shared and then they should be logged in to the app (anonymously via Firebase in case you are wondering), and the users location information should print to the console. The prompt works, however after the allow location button is hit, my application terminates due to uncaught exception 'NSInternalInconsistencyException'
and the reason is that the 'Delegate must respond to locationManager:didUpdateLocations:'
Which is strange because I do have a didUpdateLocations:
function in my code.
I have no idea what is going on, and any help is appreciated. My ViewController code is below, thanks!
/*
* Copyright (c) 2016 Ahad Sheriff
*
*/
import UIKit
import Firebase
import CoreLocation
class LoginViewController: UIViewController {
// MARK: Properties
var ref: FIRDatabaseReference! // 1
var userID: String = ""
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
ref = FIRDatabase.database().reference() // 2
}
@IBAction func loginDidTouch(sender: AnyObject) {
//ask user to enable location services
locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
//collect user's location
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
locationManager.requestLocation()
locationManager.startUpdatingLocation()
let location = self.locationManager.location
var latitude: Double = location!.coordinate.latitude
var longitude: Double = location!.coordinate.longitude
print("current latitude :: \(latitude)")
print("current longitude :: \(longitude)")
//Log in to application anonymously using Firebase
FIRAuth.auth()?.signInAnonymouslyWithCompletion() { (user, error) in
if let user = user {
print("User is signed in with uid: ", user.uid)
self.userID = user.uid
} else {
print("No user is signed in.")
}
self.performSegueWithIdentifier("LoginToChat", sender: nil)
}
}
else {
print("Unable to determine location")
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
super.prepareForSegue(segue, sender: sender)
let navVc = segue.destinationViewController as! UINavigationController // 1
let chatVc = navVc.viewControllers.first as! ChatViewController // 2
chatVc.senderId = userID // 3
chatVc.senderDisplayName = "" // 4
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!)
{
//--- CLGeocode to get address of current location ---//
CLGeocoder().reverseGeocodeLocation(manager.location!, completionHandler: {(placemarks, error)->Void in
if (error != nil)
{
print("Reverse geocoder failed with error" + error!.localizedDescription)
return
}
if placemarks!.count > 0
{
let pm = placemarks![0] as CLPlacemark
self.displayLocationInfo(pm)
}
else
{
print("Problem with the data received from geocoder")
}
})
}
func displayLocationInfo(placemark: CLPlacemark?)
{
if let containsPlacemark = placemark
{
//stop updating location
locationManager.stopUpdatingLocation()
let locality = (containsPlacemark.locality != nil) ? containsPlacemark.locality : ""
let postalCode = (containsPlacemark.postalCode != nil) ? containsPlacemark.postalCode : ""
let administrativeArea = (containsPlacemark.administrativeArea != nil) ? containsPlacemark.administrativeArea : ""
let country = (containsPlacemark.country != nil) ? containsPlacemark.country : ""
print(locality)
print(postalCode)
print(administrativeArea)
print(country)
}
}
func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
print("Error while updating location " + error.localizedDescription)
}
}
First, add explicitly that you confirm CLLocationManagerDelegate
:
class LoginViewController: UIViewController, CLLocationManagerDelegate
Second, set up delegate property for CLLocationManager
:
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
ref = FIRDatabase.database().reference()
}
Third, in CLLocationManagerDelegate
doc I see different from your declaration of didUpdateLocations
and didFailWithError
method:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
func locationManager(_ manager: CLLocationManager, didFailWithError error: NSError)
Also, you have few other issues in code. They are all about unsafe unwrapping. You should not use unsafe unwrapping !
everywhere. Doing this you are killing optionals philosophy. Doing right thing, on other hand, can drastically increase stability of your app. In loginDidTouch
function make following corrections:
var latitude: Double? = location?.coordinate.latitude
var longitude: Double? = location?.coordinate.longitude
When you call this function first time, your location not determined yet (it will be determined asynchronously, you will get location in delegate method), thats why you have fatal error when used force unwrapp. In didUpdateLocations
function:
if let pm = placemarks?.first
{
self.displayLocationInfo(pm)
}
correction of this part of code is shorter then your original code, and prevents you from two possible crashes at one time - when placemarks
is nil and when placemarks
is not nil, but empty array
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With