GeoFire + Swift+ Firebase : Save current location - swift

This is working:
geoFire.setLocation(CLLocation(latitude: 37.7853889, longitude: -122.4056973),
forKey: "firebase-hq") { (error) in
if (error != nil) {
println("An error occured: \(error)")
} else {
println("Saved location successfully!")
}
}
But how do I send my current location coordinate to my Firebase? Like this? but currentLocation doesn't work.
geoFire!.setLocation(currentLocation, forKey: "firebase-hq") { (error) in
if (error != nil) {
print("An error occured: \(error)")
} else {
print("Saved location successfully!")
}

Unless it is completely necessary I would recommend not using GeoFire and instead use apple's inbuilt API. First, open your info.plist file as source code and add the following to the file
<key>NSLocationAlwaysUsageDescription</key>
<string>Will you allow this app to always know your location?
</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Do you allow this app to know your current location?</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Will you allow this app to always know your current location?
</string>
Then add this code to your new viewcontroller:
import UIKit
import CoreLocation
class SearchViewController: UIViewController, CLLocationManagerDelegate {
var locationManager:CLLocationManager!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
determineMyCurrentLocation()
}
func determineMyCurrentLocation() {
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.startUpdatingLocation()
//locationManager.startUpdatingHeading()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation:CLLocation = locations[0] as CLLocation
// Call stopUpdatingLocation() to stop listening for location updates,
// other wise this function will be called every time when user location changes.
// manager.stopUpdatingLocation()
print("user latitude = \(userLocation.coordinate.latitude)")
print("user longitude = \(userLocation.coordinate.longitude)")
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error)
{
print("Error \(error)")
}
}
This will print the your latitude and longitude as the default current location in your simulator as somewhere in San Francisco. To change this, go into you simulator and go into the debug menu and click location, current location and enter your preferred new values.

Related

Swift - Location Manager not available right away

So basically when the app loads it's supposed to center the map on the user location, but sometimes will get stuck at the guard let. Here is the code:
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
locationManager.delegate = self
configureLocationServices()
addDoubleTap()
}
//Center map around user location
func centerMapOnUserLocation(){
print("In \(#function)")
guard let coordinate = locationManager.location?.coordinate else { print("Error getting coordinate"); return}
let coordinateRegion = MKCoordinateRegion.init(center: coordinate, latitudinalMeters: locationZoomRadius, longitudinalMeters: locationZoomRadius)
mapView.setRegion(coordinateRegion, animated: true)
//Setting local latitude and longitude variables to current location
latitude = "\(coordinate.latitude)"
longitude = "\(coordinate.longitude)"
previewDataOnButton()
print("Centered Map")
}
// MARK: - Location Services
//Request to enable location services
func configureLocationServices(){
if authorizationStatus == .notDetermined{
locationManager.requestAlwaysAuthorization()
} else {
return
}
}
//If authorization changes then call centerMapOnUserLocation
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
print("Authorization changed in \(#function). Calling centerMapOnUserLocation")
centerMapOnUserLocation()
}
}
It seems to get stuck at the guard let in centerMapOnUserLocation(). This is shown through print statements:
Authorization changed in locationManager(_:didChangeAuthorization:). Calling
centerMapOnUserLocation
In centerMapOnUserLocation()
Error getting coordinate
Im not sure how to fix this. Like I said sometimes it passes by the guard let and sometimes it gets stuck.
In you viewDidLoad add this line:
locationManager.startUpdatingLocation()
Then use this CLLocationManagerDelegate method:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
centerMapOnUserLocation()
}
You could pass in the locations into your method and move guard let before passing the location like this:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
centerMapOnUserLocation(location: location.last)
}

Xcode Swift, Geo Location won't show where i am

any one understand why Geo location won't show me where i am, also the permissions don't work over..... I'm baffled... laptop location is fine, also the app permissions are also set, also the Allow permissions tap, box for the user won't appear either
(iv explained this all above and it won't post unless i type more stuff in the box, but could some one explain why this code doesn't work norrr does it display no errors at all, i had also checked - DEBUG - locations... still nothing
import UIKit
import MapKit
import CoreLocation
class MapScreen: UIViewController {
#IBOutlet weak var mapView: MKMapView!
let locationManager = CLLocationManager()
let regionInMeters: Double = 10000
override func viewDidLoad() {
super.viewDidLoad()
checkLocationServices()
}
func setupLocationManager() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
}
func centerViewOnUserLocation() {
if let location = locationManager.location?.coordinate {
let region = MKCoordinateRegion.init(center: location, latitudinalMeters: regionInMeters, longitudinalMeters: regionInMeters)
mapView.setRegion(region, animated: true)
}
}
func checkLocationServices() {
if CLLocationManager.locationServicesEnabled() {
setupLocationManager()
checkLocationAuthorization()
} else {
// Show alert letting the user know they have to turn this on.
}
}
func checkLocationAuthorization() {
switch CLLocationManager.authorizationStatus() {
case .authorizedWhenInUse:
mapView.showsUserLocation = true
centerViewOnUserLocation()
locationManager.startUpdatingLocation()
break
case .denied:
// Show alert instructing them how to turn on permissions
break
case .notDetermined:
locationManager.requestWhenInUseAuthorization()
case .restricted:
// Show an alert letting them know what's up
break
case .authorizedAlways:
break
}
}
}
extension MapScreen: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
let region = MKCoordinateRegion.init(center: location.coordinate, latitudinalMeters: regionInMeters, longitudinalMeters: regionInMeters)
mapView.setRegion(region, animated: true)
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
checkLocationAuthorization()
}
}
Did you add the permission strings to your info.plist?
You need to add explanations for these otherwise the permission prompt won't appear, and no location updates will be send to your app.
NSLocationAlwaysUsageDescription and NSLocationWhenInUseUsageDescription
You should also get an error for this in your console so check that as well.

How to access user coordinates

I am trying to access the users coordinates using:
import MapKit
import CoreLocation
override func viewDidLoad() {
super.viewDidLoad()
let locationManager = CLLocationManager()
let userCoordinates = (locationManager.location?.coordinate)!
}
However, it crashes upon the simulator loading. I set the simulator location to Apple, and input privacy keys into info.plist, but I am not sure why this is not grabbing the user location.
There are a few things you need to do first before you can start using a device's current geolocation safely and based on the code you provided I'm going to assume that you might be missing some so here is a common set up using a Google Map, which acts like any other basically:
class YourViewController: UIViewController, CLLocationManagerDelegate {
// properties
var locationManager = CLLocationManager()
var currentCoordinate: CLLocationCoordinate2D?
// load view
override func loadView() {
addLocationManager()
}
// add location manager
func addLocationManager() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
// location manager delegate: did change authorization
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch status {
case .restricted:
print("LOCATION ACCESS RESTRICTED")
case .denied:
print("LOCATION ACCESS DENIED")
case .notDetermined:
print("LOCATION ACCESS NOT DETERMINED")
case .authorizedAlways:
fallthrough
case .authorizedWhenInUse:
print("LOCATION STATUS GRANTED")
}
}
// location manager delegate: did fail with error
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
locationManager.stopUpdatingLocation()
print("LOCATION ACCESS ERROR: \(error)")
}
// location manager delegate: did update locations
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let lastLocation = locations.last!
// unhide map when current location is available
if mapView.isHidden {
mapView.camera = GMSCameraPosition.camera(withLatitude: lastLocation.coordinate.latitude, longitude: lastLocation.coordinate.longitude, zoom: 18, bearing: 0, viewingAngle: 0)
mapView.isHidden = false
}
// update current location properties
currentCoordinate = lastLocation.coordinate
}
}
And no need to import CoreLocation if you've imported MapKit.

CLLocation + Weather(Alamofire) issues

I am trying to use CLLocation to capture longitude and latitude and then use the longitude and latitude in Alamofire to get weather. Every time, the longitude and latitude won't stop updating and the weather data won't print(if you wanna check it out here's an example link of the data: http://forecast.weather.gov/MapClick.php?lat=37.33233141&lon=-122.0312186&FcstType=json)
class SampleViewController: UIViewController, CLLocationManagerDelegate {
var locationManager:CLLocationManager!
var startLocation: CLLocation!
var isFetchingWeather = false
override func viewDidLoad() {
super.viewDidLoad()
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
}
override func viewDidAppear(_ animated: Bool) {
getCurrentLocation()
}
func getCurrentLocation(){
if CLLocationManager.locationServicesEnabled(){
locationManager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
var userLocation:CLLocation = locations[0]
if isFetchingWeather != false{
print("user latitude = \(userLocation.coordinate.latitude)")
print("user longitude = \(userLocation.coordinate.longitude)")
let requestLink = "http://forecast.weather.gov/MapClick.php?lat=\(userLocation.coordinate.latitude)&lon=\(userLocation.coordinate.longitude)&FcstType=json"
print(requestLink)
Alamofire.request(requestLink).validate().responseJSON
{ response in
switch response.result {
case .success(let data):
let json = JSON(data)
self.weatherData = json["data"].arrayValue
for weather in self.weatherData{
let temp = weather["weather"].stringValue
self.weatherString.append(temp)
}
print (self.weatherString)
if self.startLocation == nil {
self.startLocation = userLocation as! CLLocation
self.locationManager.stopUpdatingLocation()
}
case .failure(let error):
print(error)
}
}
}
else{
print("is fetching weather is false")
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error)
{
print("Error \(error)")
}
}
Thanks.
You really shouldn't run your weather request inside your location delegate. Instead, get your location in the didUpdateLocations delegate and save it to a var. Next, call stopUpdatingLocation() then call a separate function to make your weather request. Something like this:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let newLocation = locations.last
//check accuracy and timestamp of location to make sure its not a cached/old location (if you don't care about accuracy or time, you can remove this check)
let timeDiff = newLocation?.timestamp.timeIntervalSinceNow
if timeDiff < 5.0 && (newLocation?.horizontalAccuracy)!<=self.accuracyNeeded{
//stop updating location
self.locationManager.stopUpdatingLocation()
//set currentUserLocation
self.myLocation=newLocation?.coordinate
//call function to get weather
//remove delegate
self.locationManager.delegate = nil
}
}
Set a flag to indicate when you start fetching weather info and do not call Alamofire to fetch weather information if that flag is set. For example, you would declare something like after the line where you declare startLocation:
var isFetchingWeather = false
Then, in locationManagerdidUpdateLocations first check if isFetchingWeather is false. If not, return. Otherwise, fetch the weather info.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if isFetchingWeather {
return
}
isFetchingWeather = true
// Do the actual weather fetching
}
Of course, you might want to do the actual fetching of the weather after you've gotten a few location updates since the initial ones might not be that accurate :)

Error: 'Delegate must respond to locationManager:didUpdateLocations:' when collecting user location

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