My goal was to determine the city or town name of a user to fill a region text field automatically in iOS app. At first I tried to use Core Location and MapKit, but I got only city names in English, for example, ‘Ufa’. I had to get city names in Russian, so I decided to use Yandex Maps API. This is a final result – interface with a button to get city name automatically in Russian and a textField with a city name:
First, we add CLLocationManagerDelegate interface and an instance variable of CLLocationManager to get coordinates to our View Controller:
@interface MyViewController : UIViewController{ CLLocationManager *_locationManager; } @end
I created in my ViewController two helper methods to show and hide spinner without writing implementation of it:
- (void)showSpinner
{
self.hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
}
- (void)hideSpinner
{
[self.hud hide:YES];
}
I have a button on my ViewController. When it’s clicked a TextField is filled by a city name automatically after it is determined.
#pragma mark - Location determination
// Button click handler
- (void)determineRegionClicked:(id)sender
{
[self showSpinner];
// We create CLLocationManager and start updating location
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
_locationManager.distanceFilter = kCLDistanceFilterNone;
_locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[_locationManager startUpdatingLocation];
}
Here we tell, that our ViewController is a delegate of CLLocationManager.
This is a delegate method, that does all the remaining work. It is called after coordinates are determined. I used SBJson framework to parse JSON.
#import
...
#define YANDEX_MAPS_API_DOMAIN @"http://geocode-maps.yandex.ru/1.x/"
...
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
// Switching off location updates
[_locationManager stopUpdatingLocation];
// Reverse geocoding using Yandex Maps API
CLLocationCoordinate2D coordinate = newLocation.coordinate;
NSString *coordinatesString = [NSString stringWithFormat:@"%lf,%lf", coordinate.longitude, coordinate.latitude];
// Creating HTTP request. Here kind=locality means that we will be searching for a city.
NSString *urlString = [NSString stringWithFormat:@"%@?geocode=%@&results=1&lang=ru-RU&format=json&kind=locality", YANDEX_MAPS_API_DOMAIN, coordinatesString];
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
// Sending synchronous request in background queue using dispatch queues
dispatch_queue_t backgroundQueue = dispatch_queue_create("bgQueue", nil);
dispatch_async(backgroundQueue, ^{
__autoreleasing NSHTTPURLResponse *urlResponse = nil;
__autoreleasing NSError *error = nil;
NSData *resultData = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&urlResponse error:&error];
// Parsing a result
if (! error) {
NSLog(@"Status Code: %d %@", [urlResponse statusCode], [NSHTTPURLResponse localizedStringForStatusCode:[urlResponse statusCode]]);
NSString *resultString = [[NSString alloc] initWithData:resultData encoding:NSUTF8StringEncoding];
// NSLog(@"resultString:%@", resultString);
SBJsonParser *jsonParser = [[SBJsonParser alloc] init];
NSDictionary *jsonObject = [jsonParser objectWithString:resultString];
// If count is zero, then region is not found
int count = [jsonObject[@"response"][@"GeoObjectCollection"][@"metaDataProperty"][@"GeocoderResponseMetaData"][@"found"] integerValue];
if (count == 0) {
dispatch_async(dispatch_get_main_queue(), ^{
[self showRegionNotFoundError];
});
return;
}
// Searching in a JSON
NSString *city = [jsonObject[@"response"][@"GeoObjectCollection"][@"featureMember"] firstObject][@"GeoObject"][@"name"];
NSLog(@"Yandex API City returned: %@", city);
dispatch_async(dispatch_get_main_queue(), ^{
// Here I have a code that updates my UI after city is determined
});
}
else {
// If failed to get data from Yandex API
NSLog(@"An error occured, Status Code: %i", urlResponse.statusCode);
NSLog(@"Description: %@", [error localizedDescription]);
NSLog(@"Response Body: %@", [[NSString alloc] initWithData:resultData encoding:NSUTF8StringEncoding]);
dispatch_async(dispatch_get_main_queue(), ^{
[self showRegionNotFoundError];
});
}
dispatch_async(dispatch_get_main_queue(), ^{
[self hideSpinner];
});
});
}
This is an error handling for Core Location:
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
[_locationManager stopUpdatingLocation];
[self showRegionNotFoundError];
}
This method shows an error if we failed to determine city automatically:
- (void)showRegionNotFoundError
{
[self hideSpinner];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error" message:@"Failed to determine your city automatically, please, select the one from a list." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
[alertView show];
}
The same goal can be achieved using MapKit instead of Yandex Maps API for English city names:
#import#import #import ... - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation { ... CLGeocoder *geocoder = [[CLGeocoder alloc] init] ; [geocoder reverseGeocodeLocation:newLocation completionHandler:^(NSArray *placemarks, NSError *error) { [self hideSpinner]; if (! error ) { CLPlacemark *placemark = [placemarks objectAtIndex:0]; NSString *city = [[placemark addressDictionary] objectForKey:(NSString *)kABPersonAddressCityKey]; NSLog(@"determinedRegion: %@", city); NSLog(@"Full placemark address dictionary: %@", placemark.addressDictionary); } else { [self showRegionNotFoundError]; NSLog(@"Geocode failed with error %@", error); NSLog(@"\nCurrent Location Not Detected\n"); } }];
Finally, some test locations. Replace the appropriate string to simulate another coordinates. This could be done in Simulator settings also in Debug/Geo section.
// Test for Novgorod // NSString *coordinatesString = [NSString stringWithFormat:@"%lf,%lf", 44.0000f, 56.3333f]; // Test for Ufa city // NSString *coordinatesString = [NSString stringWithFormat:@"%lf,%lf", 55.9667f, 54.7500f]; // Test for a small town Meleuz in the Republic of Bashkortostan, Russia // NSString *coordinatesString = [NSString stringWithFormat:@"%lf,%lf", 55.9333f, 52.9500f]; // Test for a random location on a map // NSString *coordinatesString = [NSString stringWithFormat:@"%lf,%lf", 56.9333f, 53.9500f]; // Test for Moscow // NSString *coordinatesString = [NSString stringWithFormat:@"%lf,%lf", 37.6167f, 55.7500f];