Corelocation - didUpdateLocations is never called - ios

I am learning how to get location information on my ios14. However, didUpdateLocations is never called. I have set NSLocationAlwaysUsageDescription and NSLocationWhenInUseUsageDescription. When I test this demo on my phone, no prompt jumps out asking my permission of location. I can guarantee my function startGetLocation is called.
<key>NSLocationAlwaysUsageDescription</key>
<string>Find location</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Find location</string>
var myLocationManager: CLLocationManager?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
myLocationManager = CLLocationManager()
self.myLocationManager?.delegate = self
self.myLocationManager?.desiredAccuracy = kCLLocationAccuracyBest
self.myLocationManager?.requestAlwaysAuthorization()
}
func startGetLocation() {
self.myLocationManager?.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations:[CLLocation]) {
if let currentLocation = locations.last {
print("Altitude: \(currentLocation.altitude)")
}
}

Related

My Current Location Shows a Different Location, Google Maps - Xcode

I have implemented Google Maps but my Current Location is always USA. I am testing it on REAL device and i have location permissions added in info.plist. Google API key is also implemented in didFinishLaunchingWithOptions method of AppDelegate.
<key>NSLocationWhenInUseUsageDescription</key>
<string>By accessing your location, this app can help you navigate between different locations.</string>
Here is my code. I'm following this tutorial by raywenderlich.com. https://www.raywenderlich.com/7363101-google-maps-ios-sdk-tutorial-getting-started I have downloaded the material of this tutorial, used my API Key but still my current location is always USA. Tested on multiple real devices.
import UIKit
import GoogleMaps
import CoreLocation
class MapViewController: UIViewController {
#IBOutlet weak var mapView : GMSMapView!
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
if CLLocationManager.locationServicesEnabled() {
locationManager.requestWhenInUseAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
locationManager.delegate = self
self.mapView.delegate = self
} else {
locationManager.requestWhenInUseAuthorization()
}
}
}
CLLocationManagerDelegate:-
extension MapViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
guard status == .authorizedWhenInUse else { return }
locationManager.startUpdatingLocation()
self.mapView.isMyLocationEnabled = true
self.mapView.settings.myLocationButton = true
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.first else { return }
// the coordinates i get here are always from some city in USA.
mapView.camera = GMSCameraPosition(
target: location.coordinate,
zoom: 15,
bearing: 0,
viewingAngle: 0)
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Error: \(error)")
}
}
Please check the settings below as screenshot :

Open SwiftUI view only if location permission granted

I have a view (say V) in which a user answers a few questions and their location is recorded. However, the answers only make sense with the user's location.
So what I want is that when the user clicks on a button on the parent view, it takes them to V and immediately asks them for the location permission. If they accept, they can continue on to answer the questions, but if they deny, they navigate back to the parent screen.
I know I can navigate back to the parent screen with self.presentation.wrappedValue.dismiss().
But how do I know when the user has accepted or denied the permission since requestWhenInUseAuthorization() is an asynchronous function?
I'm following this tutorial on getting a user's location on iOS with Swift.
Code for my LocationService:
import CoreLocation
protocol LocationServiceDelegate {
func didFetchCurrentLocation(_ location: GeoLocation)
func fetchCurrentLocationFailed(error: Error)
}
class LocationService: NSObject, CLLocationManagerDelegate {
let locationManager = CLLocationManager()
var delegate: LocationServiceDelegate
init(delegate: LocationServiceDelegate) {
self.delegate = delegate
super.init()
self.setupLocationManager()
}
private func setupLocationManager() {
if canUseLocationManager() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
}
}
func requestLocation() {
if canUseLocationManager() {
print(CLAuthorizationStatus.self)
locationManager.requestWhenInUseAuthorization()
locationManager.requestLocation()
}
}
func requestPermission() {
locationManager.requestWhenInUseAuthorization()
}
private func canUseLocationManager() -> Bool {
return CLLocationManager.locationServicesEnabled()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print(locations)
if let location = locations.last {
let geoLocation = GeoLocation(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
delegate.didFetchCurrentLocation(geoLocation)
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error)
delegate.fetchCurrentLocationFailed(error: error)
}
deinit {
locationManager.stopUpdatingLocation()
}
}
struct GeoLocation {
var latitude: Double
var longitude: Double
}
CLLocationManagerDelegate has also the following method:
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
}
This method is called every time the authorization status changed. I would also like to recommend you implementing your LocationService as an ObservableObject instead of using delegate approach.

didUpdateLocations called every time when viewWillAppear for Significant Location Change

I am using CLLocationManager and MKMapView in same UIViewController.
I want to call an API only when the significant location change.
import UIKit
import CoreLocation
import MapKit
class ViewController: UIViewController,CLLocationManagerDelegate,MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.locationManager.requestAlwaysAuthorization()
self.locationManager.delegate = self
self.locationManager.startMonitoringSignificantLocationChanges()
self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
self.locationManager.distanceFilter = 500
mapView.showsUserLocation = true
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("locations \(locations)")
}
}
In this, the main issue is whenever I make the app background and foreground didUpdateLocations get called. I want it to be called only when significant location change, not every time when viewWillAppear called.
I found its because of MKMapView, didUpdateLocations is being called.
You can manually check the distance with last saved location. Try this.
var lastLocation: CLLocation?
public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let lastLocation = lastLocation, let newLocation = locations.last {
if (lastLocation.distance(from: newLocation) < manager.distanceFilter) {
return
}
}
print("locations \(locations)")
lastLocation = locations.last
}
You can save the last locations and check inside didUpdateLocations method, if not same the take appropriate action.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location : CLLocation = locations.last!
if location.coordinate.latitude != lastLat && location.coordinate.longitude != lastLon{
// take action
}
}

CLLocationManager delegate methods are not getting called(google maps is integrated)

The delegate Methods of CLLocationManager
didChangeAuthorizationStatus
and
didUpdateToLocation
are not getting called.
Location Always Usage Description key is already added in info.plist and I am getting notification also when i launch app for the first time.
I am able to see the google map, but i am not able to see current location, When i change location, its not getting updated. Basicaaly delegate methods are not getting called.
//code
import UIKit
import GoogleMaps
class ViewController: UIViewController,GMSMapViewDelegate {
#IBOutlet weak var mapViewTest: GMSMapView!
let locationManager = CLLocationManager()
var currentLocation :CLLocation = CLLocation(latitude: 0.0, longitude: 0.0)
var currentLatitude : Double = 0.0
var currentLongitude : Double = 0.0
override func viewDidLoad()
{
super.viewDidLoad()``
locationManager.delegate = self
if (CLLocationManager.locationServicesEnabled())
{
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.allowsBackgroundLocationUpdates = true
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
// Do any additional setup after loading the view, typically from a nib.
}
}
extension ViewController : CLLocationManagerDelegate
{
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus)
{
if status == .authorizedAlways
{
if(CLLocationManager .locationServicesEnabled())
{
locationManager.startUpdatingLocation()
mapViewTest.isMyLocationEnabled = true
mapViewTest.settings.myLocationButton = true
}
}
}
func locationManager(manager: CLLocationManager, didUpdateToLocation newLocation: CLLocation, fromLocation oldLocation: CLLocation)
{
mapViewTest.camera = GMSCameraPosition(target: (newLocation.coordinate), zoom: 15, bearing: 0, viewingAngle: 0)
currentLocation = newLocation
currentLatitude = newLocation.coordinate.latitude
currentLongitude = newLocation.coordinate.longitude
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError)
{
print("Errors: " + error.localizedDescription)
}
}
From your code you are working with Swift 3, and in Swift 3 CLLocationManagerDelegate method's signature is changed like this.
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
}
//func locationManager(_ manager: CLLocationManager, didUpdateTo newLocation: CLLocation,
from oldLocation: CLLocation) is deprecated with below one
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
}
Check Apple Documentation on CLLocationManagerDelegate for more details.
After checking your code i found some changes you needs to do as follow,
note : I have only added the code having issue here that of location manager
import UIKit
import CoreLocation
class ViewController: UIViewController {
let locationManager = CLLocationManager()
var currentLocation :CLLocation = CLLocation(latitude: 0.0, longitude: 0.0)
var currentLatitude : Double = 0.0
var currentLongitude : Double = 0.0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
locationManager.delegate = self
if (CLLocationManager.locationServicesEnabled())
{
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.allowsBackgroundLocationUpdates = true
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension ViewController : CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
}
func locationManager(_ manager: CLLocationManager, didFinishDeferredUpdatesWithError error: Error?) {
print("Errors: " + (error?.localizedDescription)!)
}
}
Also add below lines in .plist file if not added,
<key>NSLocationWhenInUseUsageDescription</key>
<string>$(PRODUCT_NAME) location use</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>$(PRODUCT_NAME) always uses location </string>
Add these two properties in your info.plist
NSLocationWhenInUseUsageDescription
NSLocationAlwaysUsageDescription
you need to put the mapview delegate to self.
try this:
override func viewDidLoad() {
super.viewDidLoad()
// User Location Settings
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
// Google Maps Delegate
mapView.delegate = self
}
// View will appear
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Google maps settings
mapView.isMyLocationEnabled = true
mapView.settings.myLocationButton = true
// Get location if autorized
if (CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedWhenInUse) {
let (latitude, longitude) = self.getLocation()
mapView.camera = GMSCameraPosition.camera(
withLatitude: latitude,
longitude: longitude,
zoom: 14)
}
}
//Get the user location
func getLocation() -> (latitude: CLLocationDegrees, longitude: CLLocationDegrees) {
let latitude = (locationManager.location?.coordinate.latitude)!
let longitude = (locationManager.location?.coordinate.longitude)!
return (latitude, longitude)
}

unable to see locations: [CLLocation]

info.plist file:
<key>NSLocationWhenInUseUsageDescription</key>
<string>This project would like to know your location</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>location when app is in background</string>
As shown above, have added the key/value pairs for the info.plist
The problem i'm facing is that the first time i ran the app it prompted me for authorization, but, on subsequent runs the popup hasn't appeared.
second, I'm trying to print the locations from
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) using the print(location) but nothing is printed.
in the debug options i have set location as "city bicycle ride"
The code i'm using is as follows.
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, MKMapViewDelegate , CLLocationManagerDelegate {
#IBOutlet var map: MKMapView!
//use the locManager to get the users location
var locManager = CLLocationManager()
override func viewDidLoad() {
locManager.delegate = self
locManager.desiredAccuracy = kCLLocationAccuracyBest
locManager.requestWhenInUseAuthorization()
super.viewDidLoad()
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print(locations)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
iOS will ask only once for location authorization. If the user denies, he will have to change this in the device settings manually. If he agrees, you will be able to obtain the location until he blocks it in the device settings.
So this code:
locManager.requestWhenInUseAuthorization()
will prompt a popup only 1 time. The problem is that you probably forget to call:
locManager.startUpdatingLocation()
You need to call this every time you want to start obtaining the location.
use this
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.requestLocation()
locationManager.requestWhenInUseAuthorization()
locationManager.requestAlwaysAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}

Resources