'How to stop multiple times method calling of didUpdateLocations() in ios

This my code......

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

    location_updated = [locations lastObject];
    NSLog(@"updated coordinate are %@",location_updated);
    latitude1 = location_updated.coordinate.latitude;
    longitude1 = location_updated.coordinate.longitude;

    self.lblLat.text = [NSString stringWithFormat:@"%f",latitude1];
    self.lblLon.text = [NSString stringWithFormat:@"%f",longitude1];

    NSString *str = [NSString stringWithFormat:@"https://maps.googleapis.com/maps/api/geocode/json?latlng=%f,%f&sensor=false",latitude1,longitude1];
    url = [NSURL URLWithString:str];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    connection = [NSURLConnection connectionWithRequest:request delegate:self];
    if (connection)
    {
        webData1 = [[NSMutableData alloc]init];
    }
        GMSMarker *marker = [[GMSMarker alloc] init];
        marker.position = CLLocationCoordinate2DMake(latitude1,longitude1);
        marker.title = formattedAddress;
        marker.icon = [UIImage imageNamed:@"m2.png"];
        marker.map = mapView_;
        marker.draggable = YES;
 }

This method is call multiple times which i don't want.....



Solution 1:[1]

While allocating your LocationManager object you can set the distanceFilter property of the LocationManager. Distance filter property is a CLLocationDistance value which can be set to notify the location manager about the distance moved in meters. You can set the distance filter as follows:

LocationManager *locationManger = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.distanceFilter = 100.0; // Will notify the LocationManager every 100 meters
locationManager.desiredAccuracy = kCLLocationAccuracyBest;

Solution 2:[2]

The easiest way:

-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
   [manager stopUpdatingLocation];
    manager.delegate = nil;

   //...... do something

}

The manager can't find your didUpdateLocations method without the delegate reference :-D

But don't forget to set it again before using startUpdatingLocation

Solution 3:[3]

I have similar situation. You can use dispatch_once:

static dispatch_once_t predicate;

- (void)update
{
    if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined &&
        [_locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
        [_locationManager requestWhenInUseAuthorization];
    }

    _locationManager.delegate = self;
    _locationManager.distanceFilter = kCLDistanceFilterNone;
    _locationManager.desiredAccuracy = kCLLocationAccuracyBest;

    predicate = 0;
    [_locationManager startUpdatingLocation];
}

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
    [manager stopUpdatingLocation];
    manager = nil;

    dispatch_once(&predicate, ^{
        //your code here
    });
}

Solution 4:[4]

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

Solution 5:[5]

You can use a static variable to store the latest location timestamp and then compare it to the newest one, like this:

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    [manager stopUpdatingLocation];
    static NSDate *previousLocationTimestamp;

    CLLocation *location = [locations lastObject];
    if (previousLocationTimestamp && [location.timestamp timeIntervalSinceDate:previousLocationTimestamp] < 2.0) {
        NSLog(@"didUpdateLocations GIVE UP");
        return;
    }
    previousLocationTimestamp = location.timestamp;

    NSLog(@"didUpdateLocations GOOD");

    // Do your code here
}

Solution 6:[6]

Swift 5 :

If you are looking for a solution in swift.

I tried the accepted answer but it didn't work for me. I tried the below solution by checking the time duration between locations. if it is less than 10 seconds then it will return and the location handler will not update.

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.last else {
            return
        }
        
        let locationAge = -location.timestamp.timeIntervalSinceNow
        if locationAge > 10.0 { //10 seconds
            return
        }
        if location.horizontalAccuracy < 0 {
            return
        }
        
        self.currentLocation = location
        print("Location :- \(location.coordinate)")
        //location updated
    }

Solution 7:[7]

Write this method when ever you want to stop updating location manager

[locationManager stopUpdatingLocation];

Solution 8:[8]

for the time constraint, i did not understand code from accepted answer, posting a different approach. as Rob points out "When you first start location services, you may see it called multiple times". the code below acts on the first location, and ignores the updated locations for first 120 seconds. it is one way to address orginal question "How to stop multiple times method calling of didUpdateLocations".

in .h file:

@property(strong,nonatomic) CLLocation* firstLocation;

in .m file:

// is this the first location?
    CLLocation* newLocation = locations.lastObject;
    if (self.firstLocation) {
        // app already has a location
        NSTimeInterval locationAge = [newLocation.timestamp timeIntervalSinceDate:self.firstLocation.timestamp];
        NSLog(@"locationAge: %f",locationAge);
        if (locationAge < 120.0) {  // 120 is in seconds or milliseconds?
            return;
        }
    } else {
        self.firstLocation = newLocation;
    }

    // do something with location

Solution 9:[9]

You could set a flag (Bool). When you instantiate your locationsManager set flag = true then when locationManager:didUpdateLocations returns inside a code block that you want to run only once set flag = false. This way it will only be run the once.

 if flag == true {
     flag = false
    ...some code probably network call you only want to run the once 
    }

locations manager will be called multiple times but the code you want to execute only once, and I think that is what you are trying to achieve?

Solution 10:[10]

you can write : [manager stopUpdatingLocation]; manager = nil; in didupdatelocation delegate

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Sujith Thankachan
Solution 2 Maurice Raguse
Solution 3
Solution 4
Solution 5 marcelosalloum
Solution 6 Sourabh Sharma
Solution 7 Charan Giri
Solution 8
Solution 9
Solution 10 Pramanshu