Loading user location data for GeoFire query in viewWillAppear - ios

On my main CollectionView (the app home page), I would like to update the locations (i.e. the cells themselves) based on the closest location to the user. I am storing my locations in Firebase and will be using GeoFire and core location in order to properly list out the locations in distance order from the user.
Other posts have said that the best practice for observers involves loading it in viewWillAppear; however, I have been unable to obtain the user's location before this runs and do not have a centered point for my GeoFire query. Most examples related to CoreLocation involve maps or preselected coordinates, but I wasn't planning on using a map within this controller and want to dynamically load based off of user location.
I have researched loading didUpdateLocations within AppDelegate, but have not been able to call that method there either. What is the best way to get user location coordinates before running a GeoFire query when loading that returned data in viewWillAppear or do you need to set a region and check if a user has entered within that region?
var locationDict = [CLLocation]() //using this variable to pass values outside the function to the query
override func viewWillAppear(animated: Bool) {
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let recentLocation = locations[0]
locationDict.append(recentLocation)
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error.localizedDescription)
}

There is quite a lot missing in your code, but in short (what works for me):
.requestLocation in viewWillAppear
.startUpdatingLocation() in viewDidAppear. (so twice).
var justStartedUsingMap = true
In didUpdateLocations:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]){
guard let myLocation:CLLocation = manager.location else {return}
let location:CLLocationCoordinate2D = myLocation.coordinate
let region = MKCoordinateRegion(center: location, span: MKCoordinateSpan(latitudeDelta: 0.12, longitudeDelta: 0.12))
if justStartedUsingMap == true {
self.currentLocationSearch(myLocation)
justStartedUsingMap = false
} else {
locationManager.stopUpdatingLocation()
}
self.map.setRegion(region, animated: true)
}
func currentLocationSearch(_ location: CLLocation) {
// the geofire observer
}

Related

How to get accurate current location coordinates (latitude/longititude) in IOS Swift

I am using CoreLocation in IOS Swift to get the current coordinates but for some cases, it gives me closest to my desired location while sometimes the coordinates are many meters away from my given address. please check my code which I am using is there any other way to get an accurate current location coordinates.?
My Code is :
import CoreLocation
var locationManager:CLLocationManager!
var userLat = 0.0
var userLong = 0.0
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager = CLLocationManager()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestAlwaysAuthorization()
if CLLocationManager.locationServicesEnabled(){
self.locationManager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations:
[CLLocation]) {
guard let userLocation :CLLocation = locations.first else{return}
userLat = userLocation.coordinate.latitude
userLong = userLocation.coordinate.longitude
//current location
print("current location latitude is :%#", userLat)
print("current location longitude is :%#", userLong)
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(Error: \(error)")
}
Please help me some time current location is accurate and sometimes it's too meters away. Thanks
Assuming the user of your app has allowed you use precise locations you still need to take into account the fact that the location provided is only as accurate as the hardware can provide. It takes time for the location to get accurate and you may see several calls to didUpdateLocations as the system warms up.
You can use horizontalAccuracy and verticalAccuracy on a CLLocation to make decisions on what to do with a position update.

What to create or properly use a singleton

I would like to share the coordinates instance and use it on the
DiscoverDetailVc. I had 2 location manager instances on my app, after
couple research and reading, find out that the best approach is to create a singleton that each VC will use. I came up with the following code but the issue is that on the DiscoverDetailVC, DiscoverRestaurantTableViewController.shared.coordinates is nil however when I print the coordinates from the DiscoverRestaurantVC, I can see the coordinates value. What am I doing wrong in order to get it right.
Thanks
class DiscoverRestaurantTableViewController: UITableViewController, CLLocationManagerDelegate {
static let shared = DiscoverRestaurantTableViewController()
let locationManager = CLLocationManager()
var coordinates: CLLocationCoordinate2D?
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// Default implementation to get the coordinates
coordinates = location.coordinate
}
class DiscoverDetailViewController: UIViewController {
func getDirections (){
let coordinate = DiscoverRestaurantTableViewController.shared.coordinates
guard let location = coordinate.coordinates else {return print("booon")}
}
}
Change coordinates = location.coordinate to DiscoverRestaurantTableViewController.shared.coordinates = location.coordinate
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
//coordinates = location.coordinate
DiscoverRestaurantTableViewController.shared.coordinates = location.coordinate
}
But it is good to create new class and manage singleton objects. It doesn’t look good to create singleton within TableViewController.
Hope this helps.

how to get user latitude and longitude

I am trying to get a user's latitude and longitude values, but the CLLocationManagerDelegate's methods are not getting called. Thanks in advance to anyone who can help me solve this.
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
var locationmanager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationmanager.delegate = self;
locationmanager.desiredAccuracy = kCLLocationAccuracyBest
locationmanager.requestAlwaysAuthorization()
locationmanager.startUpdatingLocation()
}
//MARK: - location delegate methods
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation :CLLocation = locations[0] as CLLocation
print("user latitude = \(userLocation.coordinate.latitude)")
print("user longitude = \(userLocation.coordinate.longitude)")
}
}
You can typically okay. To access user's current location you need physical device.
Still, there are second way to simulate location on simulator using Simulate Location option available above console in toolbar. This option available when your application is running.
Click on it to see list of location and select location to get device location on simulator. It's will be not current location but Simulate Location will set simulator location to your selected location. See following image:
You can also add your custom GPX file to load your current location. You can also add your Customer Location from debug menu of simulator. See following image:
I hope this will help you.
import following framework
Import CoreLocation
if you want Display map
Import MapKit
You can determine user’s current location by declaring
CLLocationManager instance:
let locationManager = CLLocationManager()
In viewDidLoad() you have to instantiate the CLLocationManager class,
like so:
// Ask for Authorisation from the User.
self.locationManager.requestAlwaysAuthorization()
// For use in foreground
self.locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
}
Then in CLLocationManagerDelegate method you can get user's current
location coordinates:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let locValue: CLLocationCoordinate2D = manager.location?.coordinate else { return }
print("locations = \(locValue.latitude) \(locValue.longitude)")
}
In the info.plist you will have to add
Make sure you set following privacy permission.
Also, make sure simulator’s location service is ON.
Privacy - Location Always and When In Use Usage Description
Privacy - Location When In Use Usage Description

Prevent zooming/heading update stop in iOS MKMapView

In iOS I have an MKMapView and CLLocationManager to update the heading and location. No matter what I do, I can't get the map view to stop zooming in tightly on the user's location. I have annotations on the map that I want visible at all times, and I want the heading to update as the user spins the device. This is working using mapView.setRegion, but after a bit, the map locks at the north and no longer rotates. Below is the CLLocationManager code I'm using:
func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
if data != nil{
setRegion(locationManager?.location?.coordinate.latitude)!, longitude: (locationManager?.location?.coordinate.longitude)!), data: data!)
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if !regionSet && data != nil{
setRegion(location: CLLocationCoordinate2D(latitude: (locationManager?.location?.coordinate.latitude)!, longitude: (locationManager?.location?.coordinate.longitude)!), data: data!)
regionSet = true
}
}
setRegion calculates the extents of the map to zoom on and sets the setRegion function appropriately.
Below is setRegion. I calculate the zoomWindowDegrees variable when I load in the annotations by finding the maximum lat/long for the annotations and the use that in the function.
private func setRegion(location:CLLocationCoordinate2D, towers:[Tower]){
let center = location
let coordinateSpan = MKCoordinateSpan(latitudeDelta: zoomWindowDegrees, longitudeDelta: zoomWindowDegrees)
let region = MKCoordinateRegion(center: center, span: coordinateSpan)
mapView.setRegion(region, animated: false)
}

CLLocationCoordinates from locationManager didUpdateLocations

I am trying to use CoreLocation (once permission is granted) to get a user's CLLocationCoordinate2D, so that I can pass that information to an Uber deeplink (after a UIButton within my TableView is pressed).
I've figured out how to get the coordinates as CLLocations, and turn them into CLLocationCoordinates2D in the didUpdateLocations method, but can't seem to transfer them over to my buttonPressed function.
Can anyone explain how I can properly transfer the coordinates info to the uberButtonPressed method? I am also confused about how to get the locationManager to stop updating location once a suitable location is determined. Any help is much appreciated. By the way I am using this to instantiate Uber: https://github.com/kirby/uber
import UIKit
import CoreLocation
import MapKit
class ATableViewController: UITableViewController, CLLocationManagerDelegate {
let locationManager = CLLocationManager()
var location: CLLocation?
var coordinate: CLLocationCoordinate2D?
// Implemented tableView methods etc here...
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
let newLocation = locations.last as! CLLocation
println("DID UPDATE LOCATIONS \(newLocation)")
location = newLocation
coordinate = location!.coordinate
println("WE HAVE THE COORDINATES \(coordinate!.latitude) and \(coordinate!.longitude)") // this prints along with DID UPDATE LOCATIONS
}
func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
println("error:" + error.localizedDescription)
}
func uberButtonPressed(sender: UIButton!) {
let senderButton = sender
println(senderButton.tag)
let authStatus: CLAuthorizationStatus = CLLocationManager.authorizationStatus()
if authStatus == .NotDetermined {
locationManager.requestWhenInUseAuthorization()
return
}
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
var pickupLocation = coordinate! // fatal error: unexpectedly found nil while unwrapping an Optional value
println(pickupLocation)
// // Create an Uber instance
// var uber = Uber(pickupLocation: pickupLocation)
//
// Set a few optional properties
// uber.pickupNickname = "OK"
//
// uber.dropoffLocation = CLLocationCoordinate2D(latitude: 47.591351, longitude: -122.332271)
// uber.dropoffNickname = "whatever"
//
// // Let's do it!
// uber.deepLink()
//
}
You should move var pickupLocation = coordinate! into your didUpdateLocations. Once assigned, you can call locationManager.stopUpdatingLocation() also from inside 'didUpdateLocation or your value for coordinate with keep updating. After stopping the locationManager, call a NEW function to run the rest of your code currently in func uberButtonPressed
I had the same problem.
Instead of calling the delegate method like this:
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
debugPrint("NOT CALLED-- didUpdateLocations AnyObject")
}
I changed into:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
debugPrint("CALLED-- didUpdateLocations CLLocation")
}
The problem is that you are unwrapping coordinate which is a CLLocationCoordinate2D? with a !. Since coordinate is from location which is a CLLocation?. It is possible that coordinate is nil and location is nil, especially before location services have had a chance to kick in. Only run the rest of uberButtonPressed: for the case where coordinate and location are not nil by using if let currentCoordinate = coordinate?, and in that case, call locationManager.stopUpdatingLocation().

Resources