Search code examples
iosobjective-cxcodecllocationclgeocoder

Confused about CLGeocoder class method for reverse geocoding a location


For the past 2 hours, I have been struggling with implementing a CLGeocoder class method that is used for reverse geocoding a location.

The code that is provided for this method in the Xcode documentation under the CLGeocode class reference is this:

- (void)reverseGeocodeLocation:(CLLocation *)location completionHandler:(CLGeocodeCompletionHandler)completionHandler

I was trying to use that in my code and wasn't having any luck. So I looked over some old stackoverflow questions and found an implementation that ended up working for me.

This is the code that works for me:

-(IBAction)gpsButton {

 CLGeocoder *geocoder = [[CLGeocoder alloc]init];

 [geocoder reverseGeocodeLocation:currentLocation completionHandler:^(NSArray *placemarks, NSError *error){

  CLPlacemark *placemark = placemarks[0];
  NSLog(@"Found %@", placemark.thoroughfare);

}

 ];

}

What I don't understand is why the xcode documentation includes (CLLocation *) and (CLGeocodeCompletionHandler) when that's not really how the final code needs to look.

Is it just trying to remind me that the "location" object should be an instance of the CLLocation class and that the "completionHandler" object should be an instance of the CLGeocodeCompletionHandler class?

Is it trying to remind me that before implementing this method I need to create and initialize objects for the CLLocation class and CLGeocodeCompletionHandler class so the method can function properly? And if this is the case then why does CLLocation have a pointer in the xcode example but CLGeocodeCompletionHandler does not?

I realize that this is a "fundamentals" type question but I have gone over several of my go-to references and the way they are explaining things are really not helping me with this current confusion.

Thank you for the help.


Solution

  • You say:

    What I don't understand is why the xcode documentation includes (CLLocation *) and (CLGeocodeCompletionHandler) when that's not really how the final code needs to look.

    The thing is, this is how the final code looks. So, as you point out, the reverseGeocodeLocation:completionHandler: method is defined as such:

    - (void)reverseGeocodeLocation:(CLLocation *)location completionHandler:(CLGeocodeCompletionHandler)completionHandler;
    

    Furthermore, if you look at the definition of CLGeocodeCompletionHandler, it's defined as a block that returns void and takes two parameters, an array of placemarks and an NSError object, like so:

    typedef void (^CLGeocodeCompletionHandler)(NSArray *placemarks, NSError *error);
    

    Thus, your code sample, below, conforms exactly to this definition:

    [geocoder reverseGeocodeLocation:currentLocation completionHandler:^(NSArray *placemarks, NSError *error){
    
        CLPlacemark *placemark = placemarks[0];
        NSLog(@"Found %@", placemark.thoroughfare);
    
    }];
    

    If you're asking why the method definition of reverseGeocodeLocation:completionHandler: is using the CLGeocodeCompletionHandler typedef, rather than the actual complex block definition, they do that to simplify the code. Yes, it makes Xcode's "code completion" a little less helpful, but when you are actually writing the code that implements this method with the block parameter, it makes your life much easier.

    When you start writing your own methods that take block parameters and have classes that store these blocks in properties, you'll see that using typedef really is a godsend, eliminating a lot of simple errors that result from typos in the really awkward block syntax.

    For example, let's say you wanted to hold that block in a variable, and then use that variable in your call to reverseGeocodeLocation. Because you have a typedef, that is very simple:

    CLGeocodeCompletionHandler customHandler = ^(NSArray *placemarks, NSError *error){
        CLPlacemark *placemark = placemarks[0];
        NSLog(@"Found %@", placemark.thoroughfare);
    };
    
    [geocoder reverseGeocodeLocation:currentLocation completionHandler:customHandler];
    

    Without the typedef, you'd have to resort to a slightly more unwieldy syntax:

    void (^customHandler)(NSArray *, NSError *) = ^void(NSArray *placemarks, NSError *error) {
        CLPlacemark *placemark = placemarks[0];
        NSLog(@"Found %@", placemark.thoroughfare);
    };
    
    [geocoder reverseGeocodeLocation:currentLocation completionHandler:customHandler];
    

    This is, admittedly, a somewhat contrived example, but as you use blocks more and more, you'll start to appreciate the simplicity that the typedef provides when you have to use the same block signature again and again throughout your code.

    So, whenever you see a method that has a custom block typedef as a parameter, just drill down one more level of detail, and take a look at that typedef, and all should become clear.