Nilesh Agrawal
Nilesh Agrawal

Reputation: 3042

Storing the values from completion handler into a variable

I am trying to store a String Value inside the completion handler but its scope is restricted to that block only. How to resolve it ?

// Do any additional setup after loading the view, typically from a nib.
    CLLocationManager *locationManager = [[CLLocationManager alloc] init];
    locationManager.distanceFilter = kCLDistanceFilterNone;
    locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
    [locationManager startUpdatingLocation];


    CLGeocoder *geocoder = [[CLGeocoder alloc] init] ;
    NSString *co;
    [geocoder reverseGeocodeLocation:locationManager.location
                   completionHandler:^(NSArray *placemarks, NSError *error) {
                       NSLog(@"reverseGeocodeLocation:completionHandler: Completion Handler called!");

                       if (error){
                           NSLog(@"Geocode failed with error: %@", error);
                           return;

                       }


                       CLPlacemark *placemark = [placemarks objectAtIndex:0];



                       NSLog(@"placemark.country %@",placemark.country);
                       co = placemark.country;
                       // 
                   }];

    NSLog(@"%@",co);

At this line the value of co becomes null again. Please let me know, how can retain the value outside the completion handler, which I store inside completion handler.

Upvotes: 2

Views: 1391

Answers (3)

Sean Kladek
Sean Kladek

Reputation: 4446

The issue isn't a matter of scope, it's that the log is called before the completion block. The reverse geocode call is asynchronous. It will return the block whenever it's done with what it's doing, but in the meantime, the rest of your method will execute. If you print co the line after you set its value but within the completion block, it will display the correct value.

Example:

[geocoder reverseGeocodeLocation:locationManager.location
               completionHandler:^(NSArray *placemarks, NSError *error) {
                   NSLog(@"reverseGeocodeLocation:completionHandler: Completion Handler called!");

                   if (error){
                       NSLog(@"Geocode failed with error: %@", error);
                       return;

                   }


                   CLPlacemark *placemark = [placemarks objectAtIndex:0];



                   NSLog(@"placemark.country %@",placemark.country);
                   co = placemark.country;

                   // The completion block has returned and co has been set. The value equals placemark.country
                   NSLog(@"%@",co);
               }];
// This log is executed before the completion handler. co has not yet been set, the value is nil
NSLog(@"%@",co);

If you need to use the co variable outside of the block, you should call the methods it will be used in from within the completion block:

[geocoder reverseGeocodeLocation:locationManager.location
               completionHandler:^(NSArray *placemarks, NSError *error) {

    [self myMethodWithCountry:placemark.country];

}];

- (void)myMethodWithCountry:(NSString *)country {
    // country == placemark.country
}

Upvotes: 4

Milo
Milo

Reputation: 5091

As Julie suggested, add __block before NSString *co; i.e. __block NSString *co;. That's two underscores btw.

Upvotes: -1

Sujith Thankachan
Sujith Thankachan

Reputation: 3506

The NSLog command you wrote will work before you complete the block. Since this happens you will get null. One thing you can do is print the value of co inside the block instead of doing it outside.

OR

change the declaration of co as follows:

 __block NSString *co= nil; 

Upvotes: 2

Related Questions