LocationManager singleton didUpdateLocations not getting called - ios

I have created a singleton LocationManager class to handle location updates in my application. In the first view controller the shared instance is accessed in viewDidLoad and a call to getCurrentLocation is made. "Getting users location..." prints to the console however the delegate method didUpdateLocations on the location manager is never called and the location is not updated. I have the required keys in the info.plist and the expected permissions prompts show.
I have a feeling this is a threading issue with the LocationManager falling out of scope but I'm not sure, would be great if someone could point me in the right direction !
Location Manager:
import Foundation
import CoreLocation
class LocationManager: NSObject, CLLocationManagerDelegate {
static let shared = LocationManager()
var manager: CLLocationManager!
override init() {
super.init()
self.manager = CLLocationManager()
self.manager.delegate = self
if CLLocationManager.authorizationStatus() == .notDetermined {
self.manager.requestWhenInUseAuthorization()
}
self.manager.desiredAccuracy = kCLLocationAccuracyBest
}
public func getCurrentLocation(){
print("Getting users location...")
self.manager.startUpdatingLocation()
}
func locationManager(manager:CLLocationManager, didUpdateLocations locations:[AnyObject]) {
self.manager.stopUpdatingLocation()
print("locations = \(locations)")
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error.localizedDescription)
}}
ViewController:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var savedAreasTableView: UITableView!
#IBOutlet weak var noSavedAreasLabel: UILabel!
var manager: LocationManager! = LocationManager.shared
override func viewDidLoad() {
super.viewDidLoad()
self.noSavedAreasLabel.text = "You have no saved areas available !\nTo add some, search for a location and then favourite it."
//Check here to see if user has any favourited areas
self.savedAreasTableView.isHidden = true
self.noSavedAreasLabel.isHidden = false
self.manager.getCurrentLocation()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

The correct delegate function is this:
func locationManager(_ manager: CLLocationManager,
didUpdateLocations locations: [CLLocation])

Related

Declaration is only valid at file scope how to fix?

I have a Problem in my swift file.
Im trying to follow a tutorial on Youtube and he doesn't get the error.
Here is the code:
class ViewController: UIViewController, CLLocationManagerDelegate {
private let locationManager = CLLocationManager()
private var currentLocation: CLLocationCoordinate2D?
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
configerLocationServices()
}
private func configerLocationServices() {
locationManager.delegate = self
let status = CLLocationManager.authorizationStatus()
if status == .notDetermined {
locationManager.requestWhenInUseAuthorization()
} else if status == .authorizedAlways || status == .authorizedWhenInUse {
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
}
extension ViewController: CLLocationManagerDelegate { //Error: Declaration is only valid at file scope
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("Did get latest Lcation")
}
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
print("The Status changed")
}
}
}
I don't know what im doing wrong, has anyone a solution?
Thank you in advance.
Thats because you have declared an extension inside your class. Move it outside the class and compiler will stop complaining
class ViewController: UIViewController {
private let locationManager = CLLocationManager()
private var currentLocation: CLLocationCoordinate2D?
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
configerLocationServices()
}
private func configerLocationServices() {
locationManager.delegate = self
let status = CLLocationManager.authorizationStatus()
if status == .notDetermined {
locationManager.requestWhenInUseAuthorization()
} else if status == .authorizedAlways || status == .authorizedWhenInUse {
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
}
}
extension ViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("Did get latest Lcation")
}
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
print("The Status changed")
}
}
You have clearly confirmed to CLLocationManagerDelegate in your ViewController extension so you dont need to confirm it in class declaration (this will result in compiler giving you a warning that duplicate confirmation to protocol) so change class ViewController: UIViewController, CLLocationManagerDelegate to class ViewController: UIViewController
Remove CLLocationManagerDelegate declaration in class ViewController: UIViewController, CLLocationManagerDelegate line. Also, make sure that you set all pairs of curly braces correctly, I mean open and close in proper places

UI won't update after getting json data from Openweathermap IOS 4 Xcode 10

my JSON have been printed to console with the correct value request. but label.text and city.text won't update UI in main controller
#IBOutlet weak var icon: UIImageView!
#IBOutlet weak var date: UILabel!
#IBOutlet weak var weatherType: UILabel!
#IBOutlet weak var degree: UILabel!
#IBOutlet weak var city: UILabel!
var currentWeather : CurrentWeather!
override func viewDidLoad() {
super.viewDidLoad()
currentWeather = CurrentWeather()
}
override func viewDidAppear(_ animated: Bool) {
super .viewDidAppear(animated)
locationAuthStatus()
}
func locationAuthStatus() {
if CLLocationManager.authorizationStatus() == .authorizedWhenInUse {
currentLocaion = locationManager.location
//Location.sharedIntance.latitude = currentLocaion.coordinate.latitude
//Location.sharedIntance.longitude = currentLocaion.coordinate.longitude
print(currentLocaion)
currentWeather.downloadWeatherDetail {
downloadForecastData {
self.updateMainUI()
}
}
} else {
locationManager.requestWhenInUseAuthorization()
locationAuthStatus()
}
}
func updateMainUI() {
icon.image = UIImage(named: currentWeather.weatherType)
city.text = currentWeather.cityName
degree.text = "\(currentWeather.currentTemp)"
weatherType.text = currentWeather.weatherType
date.text = currentWeather.date
}
}
I am new to coding and 'm having a hard figuring out why isn't it showing since I confirmed that I am pulling data from Json to my console
These lines won't do what you expect.
locationManager.requestWhenInUseAuthorization()
locationAuthStatus()
Your call to requestWhenInUseAuthorization is asynchronous and should be handled by a CLLocationManagerDelegate which can be your view controller. You can do this like this:
class ViewController: UITableViewController, CLLocationManagerDelegate {
// rest of class
Add these functions to your class
func locationManager(_ manager: CLLocationManager, didUpdateLocations objects: [CLLocation]) {
let location = objects[objects.count-1]
let currentLocation = location
print(currentLocation)
currentWeather.downloadWeatherDetail {
downloadForecastData {
self.updateMainUI()
}
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
// handle error
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
locationManager.startUpdatingLocation()
}

How to track set locationManager.delegate the right way?

I tried to use CoreLocation with Swift to track the user's location:
(Below you can find the code of may ViewController.swift file.)
But the code doesn't seems working as I've expected, because I'm still getting the same error every-time I'm launching the application:
I'm sure this is the problem why I'm not able to get a result from the locationManager() function that should print out the current location.
It says "Cannot assign value of type 'ViewController' to type 'CLLocationManagerDelegate?'"
import UIKit
import CoreLocation
class ViewController: UIViewController, WKUIDelegate {
override func viewDidLoad() {
super.viewDidLoad()
if CLLocationManager.locationServicesEnabled() {
print("CLLocationManager is available")
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
locationManager.delegate = self
locationManager.startUpdatingLocation()
}
}
let locationManager = CLLocationManager()
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
print(location.coordinate)
}
}
}
Does anyone now how to fix this problem? - Any help would be very appreciated, thanks a million in advance.
You simply need to declare conformance to CLLocationManagerDelegate. You can either do this directly in the class declaration just as you did with WKUIDelegate or in an extension of ViewController.
class ViewController: UIViewController, WKUIDelegate, CLLocationManagerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
if CLLocationManager.locationServicesEnabled() {
print("CLLocationManager is available")
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
locationManager.delegate = self
locationManager.startUpdatingLocation()
}
}
let locationManager = CLLocationManager()
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
print(location.coordinate)
}
}
}
With extension:
class ViewController: UIViewController, WKUIDelegate {
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
if CLLocationManager.locationServicesEnabled() {
print("CLLocationManager is available")
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
locationManager.delegate = self
locationManager.startUpdatingLocation()
}
}
}
extension ViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
print(location.coordinate)
}
}
}

CoreLocation Delegate functions do not run until view controller viewDidLoad function is finished

This is more than likely something very easy to solve but I've been at it for some time and can't seem to get at an answer.
I would like to know why a Delegate method for CLLocationManager does not trigger until after the ViewDidLoad function when the CLLocationManager is loaded within the ViewDidLoad function.
I have set my default region to Sydney Australia within my App Scheme and I have encapsulated my locationManager within its own class as follows:
import UIKit
import CoreLocation
/* Class location is a class to track user location and return a location object. */
class usrLocation: NSObject, CLLocationManagerDelegate
{
//MARK: Properties
var locationMgr: CLLocationManager!
var location: CLLocation!
var seenError: Bool = false
//MARK: Public Methods
func startTracking() {
locationMgr = CLLocationManager()
locationMgr.delegate = self
locationMgr.desiredAccuracy = kCLLocationAccuracyBest
locationMgr.requestWhenInUseAuthorization()
locationMgr.startUpdatingLocation()
}
//Return a location object
func getLocation() -> CLLocation {
locationMgr.startUpdatingLocation()
return location!
}
//MARK: CLLocationManagerDelegate
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
locationMgr.stopUpdatingLocation()
if (seenError == false) {
seenError = true
print(error)
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
location = (locations ).last
locationMgr.stopUpdatingLocation()
}
}
I have initialised the class in my ViewController and try to begin tracking my current location in viewDidLoad.
The code looks like this.
override func viewDidLoad() {
var location = usrLocation()
override func viewDidLoad() {
super.viewDidLoad()
// Track location
location.startTracking()
location.getLocation()
//update Label text
sLongitude.text = "\(location.getLocation().coordinate.longitude)"
sLatitude.text = "\(location.getLocation().coordinate.latitude)"
}
getLocation() never returns location as it is always nil as the delegate didUpdateLocations function does not run. Why is this?
I tried to get this working using a completion handler but couldn't so my solution has been to ignore setting the label text via viewDidLoad and to instead update the labels once the location variable has been set. That way viewDidLoad completes, the delegate gets called and the labels get updated.
var location: CLLocation! {
didSet {
//update Label text
sLongitude.text = "\(location.getLocation().coordinate.longitude)"
sLatitude.text = "\(location.getLocation().coordinate.latitude)"
}
}

location manager not updating in swift ios10

I am new to swift and am trying to get a basic program that display the longitude and latitutde.
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var latLabel: UILabel!
#IBOutlet weak var longLabel: UILabel!
#IBOutlet weak var addLabel: UILabel!
var lm = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
lm = CLLocationManager()
}
#IBAction func getCurrentLocation(_ sender: AnyObject) {
lm.delegate = self
lm.desiredAccuracy = kCLLocationAccuracyBest
lm.startUpdatingLocation()
print("loc")
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print ("error")
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print ("location")
let length = locations.count
let curLoc = locations[length-0]
latLabel.text = String(curLoc.coordinate.latitude)
print ("loccation")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
You are missing two things.
you have to ask for permission using requestAlwaysAuthorization or requestWhenInUseAuthorization().
So when you are allocating your location manager in viewdidlod, Do like this
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
You need to add information as #Anbu.Karthik has suggested in his answer.
Use NSLocationAlwaysUsageDescription for apps that want to use the
device's location even when the app is not open and being used.
Use NSLocationWhenInUseUsageDescription for apps that want to use the
device's location only when the app is open and in use.
check once are you added the following information your plist
Location :
Key : Privacy - Location Always Usage Description
Value : $(PRODUCT_NAME) location use
Key : Privacy - Location When In Use Usage Description
Value : $(PRODUCT_NAME) location use
for more information see this
One other issue that I found caused the location update callback not to be called, with no obvious error message given is that it must not be a private function (which is obvious once you finally notice it, of course...). I realise the OP's one is not but this is a common place to end up if you are having issues, so it may help someone.
So the following will cause the function not to be called without giving any obvious error indication like crashing the app etc:
private func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print ("location")
let length = locations.count
let curLoc = locations[length-0]
latLabel.text = String(curLoc.coordinate.latitude)
print ("loccation")
}
Removing the 'private' keyword, as in the OP's code, will allow it to be called.

Resources