13

Same code, I'm assuming that the device is actually updating the location twice for some reason, even though I only call startUpdatingLocation() once and I run some stopUpdatingLocations() inside of didUpdateLocations

func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    manager.stopUpdatingLocation()
    let loc: CLLocation = locations[locations.count - 1]
    let id = 0
    let type = 0
    let number = 0

    createNewDataPoint(id, loc: loc, type: type, number: number)
}

In this case, createNewDataPoint gets called twice, creating 2 new datapoints. It only happens once in the simulator, so I'm assuming it has something to do with the actual device and the GPS since the simulator fakes its location.

startUpdatingLocation() is only in my code one time, on a button. Basically, you click the button, go go manager.startUpdatingLocations(), didUpdateLocations hits once on simulator, twice on device (identical coordinates) and it creates 2 new data points.

The only other code that mentions anything related is setting the accuracy, filter, authorization requests, and the previously mentioned startUpdatingLocation(). Is there something I can do to make sure I'm not creating twice as many data points as necessary?

sdouble
  • 1,055
  • 3
  • 11
  • 28
  • I think you can create a flag, turn it on if get 1 `didUpdateLocations`. I guess the problem is that the `CLLocationManager` update so fast, and `stopUpdatingLocation` need time to finish. – tuledev Nov 03 '15 at 05:40
  • 1
    Although you will still need to use one of the suggested techniques on earlier versions I suggest that you check for ios9 and use `requestLocation` in that case – Paulw11 Nov 03 '15 at 07:21

9 Answers9

18

Location Manager delegate methods can be called very frequently and at any time.

You may however, apply following algorithm to safeguard yourself:

  1. Create a global bool say didFindLocation.
  2. Set didFindLocation to false when you call startUpdatingLocation.
  3. Inside delegate call back didUpdateLocations:, if didFindLocation was false, set didFindLocation to true and then call stopUpdatingLocation.

Hope this helps.

Abhinav
  • 37,684
  • 43
  • 191
  • 309
  • Simple enough solution. Good to hear it's not a problem with my code, it's just that the location is updated so quickly that it fires off twice before I could stop it. I also noticed that a few times, it was called three times. – sdouble Nov 04 '15 at 03:55
  • but isn't it possible that the delegate didUpdateLocation might get called again before stopUpdateingLocaiton is called? I might be wrong here but just wanted to confirm. Or system will not call the delegate until the previous delegate is still in execution? Apologies if I am being silly, I am fairly new to iOS. Would it be a good design to add a lock in a delegate? – user3508953 Apr 13 '17 at 01:12
  • Best solution! It's dangerous to set delegate to nil, I think It's important keeping track from delegate's method, like `locationManager:didChangeAuthorizationStatus:`. – Thomás Pereira Sep 01 '17 at 13:10
  • @Abhinav: what if the user switches manually to restricted and then grants authorization again? Won't he find the boolean set to true? – 3000 Mar 04 '19 at 09:53
14

The best way is do as following:

func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    manager.stopUpdatingLocation()
    manager.delegate = nil
}
Andrey M.
  • 3,021
  • 29
  • 42
6

Best solution for iOS 10.0+

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
    [locationManager stopUpdatingLocation]; // stop location manager
    locationManager.delegate = nil;
    //Your logics... 
    //This will be called only one time now.
}

But don't forget to set the delegate again.

Community
  • 1
  • 1
Zumry Mohamed
  • 9,318
  • 5
  • 46
  • 51
1

After getting the desired latitude and longitude just call stopUpdatingLocation()and set the delegate to nil.

In Swift 3:

locationManager.stopUpdatingLocation() 
locationManager.delegate = nil

In Objective-C:

[locationManager stopUpdatingLocation]
locationManager.delegate = nil

Here locationManager is the object of CLLocationManager.

Dmytro Rostopira
  • 10,588
  • 4
  • 64
  • 86
iOSAnup
  • 11
  • 2
0

You will not get frequently on simulator. and on device when you will move far away then only you get didUpdateLocations. just move in a open space so GPS can identify you device location so it get best accuracy.

kalpesh
  • 1,285
  • 1
  • 17
  • 30
0

Instead of starting / ending the location update and setting delegate to nil, there is a method called requestLocation which is ideal when your application need quick fix on the user's location:

From the docs:

override func viewDidLoad() {
    // Create a location manager object
    self.locationManager = CLLocationManager()

    // Set the delegate
    self.locationManager.delegate = self
}

func getQuickLocationUpdate() {
    // Request location authorization
    self.locationManager.requestWhenInUseAuthorization()

    // Request a location update
    self.locationManager.requestLocation()
    // Note: requestLocation may timeout and produce an error if authorization has not yet been granted by the user
}

func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    // Process the received location update
}

Use this method when you want the user’s current location but do not need to leave location services running.

Whirlwind
  • 14,286
  • 11
  • 68
  • 157
0

@Zumry Mohamed 's solution is right

i try the code like this:

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {

   [self.locationManager stopUpdatingLocation];
   self.locationManager.delegate = nil;
   self.locationManager = nil; 
}

finally this delegate is called only once, i understand now why the problem is occurred, just because manager call the stopUpdatingLocationmethod but system doesn't help us to make the delegate invalid, so we can receive the callback every time location updates due to your desiredAccuracy and distanceFilter property settings of your CLLocationManager, so the final solution is just like what @Zumry Mohamed said, we can manually set the delegate to nil when we stopUpdateLocation. hope it will help you understand what happens why this could solve the problem.

SevenJustin
  • 61
  • 11
0

locationManager.startUpdatingLocation() fetch location continuously and didUpdateLocations method calls several times, Just set the value for locationManager.distanceFilter value before calling locationManager.startUpdatingLocation().

As I set 200 meters(you can change as your requirement) working fine

    locationManager = CLLocationManager()
    locationManager.delegate = self
    locationManager.desiredAccuracy = kCLLocationAccuracyBest
    locationManager.distanceFilter = 200
    locationManager.requestWhenInUseAuthorization()
    locationManager.startUpdatingLocation()
Devendra Singh
  • 717
  • 1
  • 13
  • 14
0

Another way is to set a time interval to turn on and off the delegate and so the location manager. A sorta of this

var locationManagerUpdate:Bool = false //Global

func scheduledTimerWithTimeInterval(){
    // Scheduling timer to Call the function "updateCounting" with the interval of 10 seconds

        timer = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(self.updateLocationManager), userInfo: nil, repeats: true)

}
@objc func updateLocationManager() {

    if locationManagerUpdate == false {
        locationManager.delegate = self
        locationManagerUpdate = true
    }

}
extension lm_gest: CLLocationManagerDelegate {

// Handle incoming location events.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {


    if locationManagerUpdate == true {
    manager.stopUpdatingLocation()
    manager.delegate = nil
    }
//your code here...
}
Massimo Pavanel
  • 754
  • 8
  • 7
  • When possible, please make an effort to provide additional explanation instead of just code. Such answers tend to be more useful as they help members of the community and especially new developers better understand the reasoning of the solution, and can help prevent the need to address follow-up questions. – Rajan May 15 '20 at 05:42
  • Basically it's a simple procedure to accomplish a time scheduled location manager delegate switch. It's useful to prevent the problem of the double fire of location manager itself and keep the location manager updated if the app is running. – Massimo Pavanel May 15 '20 at 09:36