didUpdateLocations only can called once - swift

func startLocationManager() {
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
locationManager.startUpdatingLocation()
}
}
After getting location authorization, I called this method in viewDidLoad(), and then thefunc locationManager(_:, didUpdateLocations:) starts to be called.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let lastLocation = locations.last!
if lastLocation.timestamp.timeIntervalSinceNow < -5 {
return
}
if lastLocation.horizontalAccuracy < 0 {
return
}
var distance = CLLocationDistance(Double.greatestFiniteMagnitude)
if let location = location {
distance = lastLocation.distance(from: location)
}
if location == nil || location!.horizontalAccuracy > lastLocation.horizontalAccuracy {
location = lastLocation
if location.horizontalAccuracy <= locationManager.desiredAccuracy {
stopLocationManager()
getWeather()
}
} else if location == nil || distance < 1 {
let timeInterval = lastLocation.timestamp.timeIntervalSince(location!.timestamp)
if timeInterval > 5 {
stopLocationManager()
getWeather()
}
}
}
And this is how I end updateLocation:
func stopLocationManager() {
locationManager.stopUpdatingLocation()
locationManager.delegate = nil
}
Finally, I got the location data I wanted perfectly.
But when I want to re-acquire the new location in other ways, that is, it has executed locationManager.startUpdatingLocation(), and still cannot execute func locationManager(_:, didUpdateLocations:).
I want to know how to fix this.

Don't set locationManager.delegate = nil when you stop updating your location. Or if you do that, set it back to self when you want to start getting location updates again.

Remove this
locationManager.delegate = nil

Related

Trying to assign Lat and Long coordinates to variables, comes back nil

I am trying to assign the value from the function that gets the current location. The print statement prints the lat and long coordinates but the variables come back nil.
I have tried moving this to view will appear but still the same results.
override func viewDidLoad() {
super.viewDidLoad()
// Ask for Authorisation from the User.
self.locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let locValue: CLLocationCoordinate2D = manager.location?.coordinate else { return }
currentLat = locValue.latitude
currentLong = locValue.longitude
print("locations = \(locValue.latitude) \(locValue.longitude)")
}
//getWeather()
}
I moved this function to another view controller in the application because it was not setting the coordinates in time when my original view would load.
I also used:
locations.last?.coordinate.longitude
As opposed to:
guard let locValue: CLLocationCoordinate2D = manager.location?.coordinate else { return }
currentLong = locValue.longitude
Im sure there is a better way to do this but this works.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
currentLong = locations.last?.coordinate.longitude
currentLat = locations.last?.coordinate.latitude
print("locations = \(currentLong!) \(currentLat!)")
}

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 :)

I am trying to get updates on location with a set interval in Swift 3, but it runs my function a lot of times each interval, it should only run once

I am getting location of the user with locationManager.startUpdatingLocation() which then calls a function that handles all my logic, problem is it seems to get called multiple times and I can't figure out why.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let locValue:CLLocationCoordinate2D = manager.location!.coordinate
print("locations = \(locValue.latitude) \(locValue.longitude)")
long = locValue.longitude
lat = locValue.latitude
if(self.arrayOfCellData.count > 0)
{
for i in 0...self.arrayOfCellData.count-1
{
getdistance(lat0: self.arrayOfCellData[i].shop.latitude!, long0: self.arrayOfCellData[i].shop.longitude!, lat1: locValue.latitude, long1: locValue.longitude)
{distance in
self.arrayOfCellData[i].meterstolocation = distance
}
}
DispatchQueue.main.async {
self.myTable.reloadData()
}
}
print("stopped updating")
locationManager.stopUpdatingLocation()
}
var run: Bool = true;
func updateLocations()
{
if(run)
{
run = false;
}
else
{
locationManager.startUpdatingLocation()
}
}
override func viewDidLoad() {
super.viewDidLoad()
//
locationManager.requestAlwaysAuthorization()
//
//
//
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.pausesLocationUpdatesAutomatically = false
locationManager.startUpdatingLocation()
Timer.scheduledTimer(timeInterval: 10,
target: self,
selector: #selector(self.updateLocations),
userInfo: nil,
repeats: true)
}
my output is:
locations = 37.33228724 -122.05833354
stopped updating
locations = 37.33228724 -122.05833354
stopped updating
locations = 37.33228724 -122.05833354
stopped updating
locations = 37.33228724 -122.05833354
stopped updating
locations = 37.33228724 -122.05833354
the output itself is correct, but it spits out multiple prints in one go. And I do not want the code to be run multiple times, only once per interval.
You should not call locationManager.stopUpdatingLocation() after you get a location, and you do not need the timer. Just call locationManager.startUpdatingLocation() once at the start and it will keep sending you updates automatically but only when the location changes. See the documentation at https://developer.apple.com/reference/corelocation/cllocationmanager

didUpdateHeading not called in swift

I am trying to get the heading information of the device in order to use the difference between the magnetic and true heading to determine the declination for the user location. For this, I have a Helper class and my MainVc (mvc). In my helper class init method I do the following:
...
...
locationManager = CLLocationManager()
switch CLLocationManager.authorizationStatus() {
case .AuthorizedAlways:
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
case .NotDetermined:
locationManager.requestAlwaysAuthorization()
case .AuthorizedWhenInUse, .Restricted, .Denied:
mvc.alertDenied();
}
if (!initialized)
{
initialized = true;
self.performSelector("finishUpdatingLocation", withObject: nil, afterDelay: 2.0);
}
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print ("didUpdateLocations")
var userLocation:CLLocation = locations[0] as! CLLocation
longitude = Float(userLocation.coordinate.longitude);
latitude = Float(userLocation.coordinate.latitude);
}
func finishUpdatingLocation () {
locationManager.stopUpdatingLocation();
NSObject.cancelPreviousPerformRequestsWithTarget(self, selector: "finishUpdatingLocation", object: nil);
mvc.goingToUpdateHeading();
locationManager.startUpdatingHeading();
}
didUpdateLocations is being called and I am successfully fetching the device's coordinates. However although I have added didUpdateHeading, the first line in it is not being printed, the device's stuck at that point:
func locationManager(manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
print("didUpdateHeading");
if (newHeading.headingAccuracy > 0) {
variation = newHeading.trueHeading - newHeading.magneticHeading;
doneLoading = true;
locationManager.stopUpdatingHeading();
mvc.goingToCalculateData();
if (calcData) {
calculateData();
print ("Calculating data");
calcData = false;
}
}
print(newHeading.magneticHeading)
}
Any ideas why the startUpdatingHeading is not being called?
You have to call locationManager.startUpdatingHeading() like
...
...
locationManager = CLLocationManager()
switch CLLocationManager.authorizationStatus() {
case .AuthorizedAlways:
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
locationManager.startUpdatingHeading()
case .NotDetermined:
locationManager.requestAlwaysAuthorization()
case .AuthorizedWhenInUse, .Restricted, .Denied:
mvc.alertDenied();
}
If you are checking it on simulator then it will not work please check it on real device.

How to request a start for startUpdatingLocation?

I'm starting a location manager as i open the view with this code:
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
var userLocation: CLLocation = locations[0] as! CLLocation
userLatitude = userLocation.coordinate.latitude
userLongitude = userLocation.coordinate.longitude
if (userLocation.horizontalAccuracy < 80) {
manager.stopUpdatingLocation()
} else {
addMapRegionAndAnnotation()
}
}
So this means the locationManager does Stop if the location is detected in a 80m radius.
Now i want to start this locationManager again if i press my updateLocation button:
#IBAction func updatePostMapView(sender: AnyObject) {
manager.startUpdatingLocation()
}
Then the app shuold update the user location till the radius is smaller then 80m again...
But as you may expect, this isn't working because i can't call the manager.startUpdatingLocation() from outside of the locationManager.
Has anyone a solution?
You can call startUpdatingLocation() or stopUpdatingLocation() from inside of any method. First You must create location manager within your class,
class
let locationManager = CLLocationManager()
func initializeLocationManager()
{
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
locationManager.pausesLocationUpdatesAutomatically = false
locationManager.distanceFilter=kCLLocationAccuracyNearestTenMeters
}
func anyMethod()
{
locationManager.stopUpdatingLocation()
locationManager.startUpdatingLocation()
}