Get current city and country from CLGeocoder? - ios

I've been all over the internet trying to find out how to get the city and country from CLGeocoder. I can get the longitude and latitude easily but I need the city and country information, and I keep running into deprecated methods and such, any ideas? It basically needs to get the location, then have an NSString for the country, and an NSString for the city, so I can use them to look up more info or put them on labels, etc.

You need to revise your terminology a bit - CLGeocoder (and most geocoders) won't give you a 'city' per-se - it uses terms such as 'Administrative Area', 'Subadministrative Area', etc. The CLGeocoder object will return an array of CLPlacemark objects which you can then query for the information you need. You init a CLGeocoder and call the reverseGeocodeLocation function with a location and a completion block. Here's an example:
if (osVersion() >= 5.0){
CLGeocoder *reverseGeocoder = [[CLGeocoder alloc] init];
[reverseGeocoder reverseGeocodeLocation:self.currentLocation completionHandler:^(NSArray *placemarks, NSError *error)
{
DDLogVerbose(#"reverseGeocodeLocation:completionHandler: Completion Handler called!");
if (error){
DDLogError(#"Geocode failed with error: %#", error);
return;
}
DDLogVerbose(#"Received placemarks: %#", placemarks);
CLPlacemark *myPlacemark = [placemarks objectAtIndex:0];
NSString *countryCode = myPlacemark.ISOcountryCode;
NSString *countryName = myPlacemark.country;
DDLogVerbose(#"My country code: %# and countryName: %#", countryCode, countryName);
}];
}
Now note that the CLPlacemark doesn't have a 'city' property. The full list of properties can be found here: CLPlacemark Class Reference

You can get city, country and iso country code using this (Swift 5):
private func getAddress(from coordinates: CLLocation) {
CLGeocoder().reverseGeocodeLocation(coordinates) { placemark, error in
guard error == nil,
let placemark = placemark
else
{
// TODO: Handle error
return
}
if placemark.count > 0 {
let place = placemark[0]
let city = place.locality
let country = place.country
let countryIsoCode = place.isoCountryCode
}
}
}

Related

How to convert CLLCoordinate2D lat/long into NSNumber

I'm trying to use Yelp API, and i'm trying to have the lat/long as the params for the API search. However, it does not take the type double, it only accepts Objective-C objects. Having no knowledge in Objective-C, what do you suggest the type for the parameter of lat and long be? I tried NSNumber, but when i try to turn my lat/long coordinates of type CLLocationCoordinate2D to an NSNumber that takes in a double, its value is nil
Here is my Yelp API that I am using:
- (void)queryTopBusinessInfoForTerm:(NSString *)term location:(NSString *)location latitude:(NSNumber *)latitude longitude:(NSNumber *)longitude completionHandler:(void (^)(NSDictionary *topBusinessJSON, NSError *error))completionHandler {
NSLog(#"Querying the Search API with term \'%#\' and location \'%#'", term, location);
//Make a first request to get the search results with the passed term and location
NSURLRequest *searchRequest = [self _searchRequestWithTerm:term location:location latitude:latitude longitude:longitude];
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithRequest:searchRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if (!error && httpResponse.statusCode == 200) {
NSDictionary *searchResponseJSON = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
NSArray *businessArray = searchResponseJSON[#"businesses"];
if ([businessArray count] > 0) {
NSDictionary *firstBusiness = [businessArray firstObject];
NSString *firstBusinessID = firstBusiness[#"id"];
NSLog(#"%lu businesses found, querying business info for the top result: %#", (unsigned long)[businessArray count], firstBusinessID);
[self queryBusinessInfoForBusinessId:firstBusinessID completionHandler:completionHandler];
} else {
completionHandler(nil, error); // No business was found
}
} else {
completionHandler(nil, error); // An error happened or the HTTP response is not a 200 OK
}
}] resume];
}
And here is the params
- (NSURLRequest *)_searchRequestWithTerm:(NSString *)term location:(NSString *)location latitude:(NSNumber *) latitude longitude:(NSNumber *)longitude {
NSDictionary *params = #{
#"term": term,
#"location": location,
#"cll": latitude,
#"cll": longitude,
#"limit": kSearchLimit
};
return [NSURLRequest requestWithHost:kAPIHost path:kSearchPath params:params];
}
And here is my current Swift method calling the Query from YelpAPi:
func yelpApi() {
var latitude = NSNumber(double: businessStreetAddress.latitude)
var longitude = NSNumber(double: businessStreetAddress.longitude)
var searchTerm: NSString = "Asian Food";
var defaultLocation: NSString = "New York"
var APISample:YPAPISample = YPAPISample();
var requestGroup:dispatch_group_t = dispatch_group_create();
APISample.queryTopBusinessInfoForTerm(searchTerm as String, location: defaultLocation as String, latitude: latitude, longitude: longitude) { (topBusinessJSON: [NSObject: AnyObject]!, error: NSError!) -> Void in
if((error) != nil) {
println("Error happened during the request" + error.localizedDescription);
} else if((topBusinessJSON) != nil) {
println("Top business info",topBusinessJSON);
} else {
println("No business was found");
}
dispatch_group_leave(requestGroup);
}
dispatch_group_wait(requestGroup, DISPATCH_TIME_FOREVER);
}
To convert the CLLocationCoordinate2D to NSNumber, You cannot have it in a single NSNumber. You can convert to two NSNumber objects like follows:
CLLocationCoordinate2D location; // This is the location you have.
NSNumber *latitude = [NSNumber numberWithDouble:location.latitude];
NSNumber *longitude = [NSNumber numberWithDouble:location.longitude];
with Modern ObjC you can covert as:
CLLocationCoordinate2D location; // This is the location you have.
NSNumber *latitude = #(location.latitude);
NSNumber *longitude = #(location.longitude);
and call your
NSURLRequest *searchRequest = [self _searchRequestWithTerm:term location:location latitude:latitude longitude:longitude];
Don't confuse with the variable named like location which just I have named it. because your function searchRequestWithTerm:location:latitude:longitude: is having one parameter named location which accepts NSString.
This may help you.

Objective-C get variable out of block

I'm struggling with getting a variable out of a block. Seems to be a pretty basic thing but I can't figure it out! How can I access it? e.g. usercity out of the block? Usercity is declared as NSString in .h.
[ceo reverseGeocodeLocation: loc completionHandler:
^(NSArray *placemarks, NSError *error) {
CLPlacemark *placemark = [placemarks objectAtIndex:0];
//NSLog(#"placemark %#",placemark);
//String to hold address
//NSString *locatedAt = [[placemark.addressDictionary valueForKey:#"FormattedAddressLines"] componentsJoinedByString:#", "];
//NSLog(#"addressDictionary %#", placemark.addressDictionary);
//NSLog(#"placemark %#",placemark.region);
NSLog(#"Land %#",placemark.country); // Give Country Name
NSLog(#"City %#",placemark.locality); // Extract the city name
NSLog(#"Adresse %#",placemark.name);
//NSLog(#"location %#",placemark.ocean);
NSLog(#"Zip %#",placemark.postalCode); //ZipCode
NSLog(#"sonstiges %#",placemark.subLocality);
//Save values in variables
usercountry = placemark.country;
usercity = placemark.locality;
userzip = placemark.postalCode;
NSLog(#"usercity: %#",usercity);
//NSLog(#"location %#",placemark.location);
}
];
Is this what you're looking for?
__block NSString *userCity;
[ceo reverseGeocodeLocation: loc completionHandler:^(NSArray *placemarks, NSError *error) {
CLPlacemark *placemark = [placemarks objectAtIndex:0];
...
userCity = placemark.locality;
}];
But if you want to actually be able to check its value outside of the block, you'll have to do so after the completion handler updates the value. Perhaps make it a property, ie. self.userCity?
Your code in the block has to store usercity where you want it. You can't "get something out" of a block, the code in the block has to do it.
You do know that a block can access all variables in the surrounding method, don't you?

Address/Zip Code as NSString to CLLocation

Is there a way to convert a zip code a user enters into a textbox and convert it into a CLLocation? I am trying to compare distance between their current location and either an address or zip code and this would be easy if I can make a CLLocation out of the NSString.
the procedure called geocoding, and that is how it looks if you implement it:
NString *_address = // any address or postcode
CLGeocoder *_geocoder = [[CLGeocoder alloc] init];
[_geocoder geocodeAddressString:_address completionHandler:^(NSArray *placemarks, NSError *error) {
if (placemarks.count > 0) {
CLPlacemark *_placemark = [placemarks firstObject];
CLLocation *_location = _placemark.location;
// ... do whaterver you want to do with the location
}
}];
NOTE: the CoreLocation.framework has to be added to your project properly. you may need to handle errors in your final completion-block, I have not added such part to my code above.

CLGeocoder returns null results for city and state when using current location data

Hi I am trying to get the name of city and state using CLGeocoder. However, placemark.addressDictionary is returning me :
{
FormattedAddressLines = (
"South Atlantic Ocean"
);
Name = "South Atlantic Ocean";
Ocean = "South Atlantic Ocean";
}
and placemark is:
South Atlantic Ocean, South Atlantic Ocean # <-42.60533670,-21.93128480> +/- 100.00m, region CLCircularRegion (identifier:'<-41.51023865,-31.60774370> radius 4958095.65', center:<-41.51023865,-31.60774370>, radius:4958095.65m)
Also, NSLog of [placemark locality] and [placemark administrativeArea] shows both nil.
Also tried adding ABAdressBookUI , ABAdressBook framework and get the data as:
NSString *addressDict = ABCreateStringWithAddressDictionary(placemark.addressDictionary, NO); which returns an object with empty description.
or tried:
NSString *state = (NSString *)[placemark.addressDictionary objectForKey:kABPersonAddressStateKey]; which throws warning of incompatible pointer types.
If there is any possible solution or if I am missing something please let me know.
My code when current location button is tapped is as follows:
CLLocation *loc;
loc = [[CLLocation alloc] initWithLatitude:[KLocationManager sharedManager]._locationManager.location.coordinate.latitude longitude:[KLocationManager sharedManager]._locationManager.location.coordinate.longitude];
CLGeocoder* geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:loc completionHandler:^(NSArray *placemarks, NSError *error) {
CLPlacemark* placemark = [placemarks objectAtIndex:0];
if (placemark) {
NSDictionary* dic = placemark.addressDictionary;
NSString* locName = [NSString stringWithFormat:#"%#, %#", [dic objectForKey:#"City"],[dic objectForKey:#"State"]];
CLLocation* loc = placemark.location;
self.returnLocationDict = #{#"name":locName, #"location":loc};
}
}];
What am I missing here?
One way of replicating what you're experiencing is to use a CLLocation with latitude and longitude of 0,0
I ran your code by using latitude:37.3318 longitude:-122.0312 (Infinite Loop..) and it worked as expected.
Your location manager is probably not returning what you expect.

Insert Latitude and Longitude Programmatically for Reverse Geolocation

I have a Situation ,where i am pulling the Latitude and Longitude from a XML file ,Parsing and Displaying it .and i am successfully be able to Do it in Two Different UILabels .but now i need to the Location and Display it in a Label.Can someone suggest me how to achieve that , i have this code , but its only let me to select the Value of Coordinates from Simulator only .
- (IBAction)geoCodeLocation:(id)sender{
//Geocoding Block
[self.geoCoder reverseGeocodeLocation: locationManager.location completionHandler:
^(NSArray *placemarks, NSError *error) {
//Get nearby address
CLPlacemark *placemark = [placemarks objectAtIndex:0];
//String to hold address
NSString *locatedAt = [[placemark.addressDictionary valueForKey:#"FormattedAddressLines"] componentsJoinedByString:#", "];
//Print the location to console
NSLog(#"I am currently at %#",locatedAt);
//Set the label text to current location
[locationLabel setText:locatedAt];
}];
}
Suggestion : this Question is not about XML parsing .
Try This
CLLocation *location = [[CLLocation alloc] initWithLatitude:37.78583400 longitude:-122.40641700];
[self.geoCoder reverseGeocodeLocation: location completionHandler:
^(NSArray *placemarks, NSError *error) {
//Get nearby address
CLPlacemark *placemark = [placemarks objectAtIndex:0];
//String to hold address
locatedAtcountry = placemark.country;
locatedAtcity = placemark.locality;
locatedAtisocountry = placemark.ISOcountryCode;
//Print the location to console
NSLog(#"Estas en %#",locatedAtcountry);
NSLog(#"Estas en %#",locatedAtcity);
NSLog(#"Estas en %#",locatedAtisocountry);
[cityLabel setText:[NSString stringWithFormat:#"%#",locatedAtcity]];
[locationLabel setText:[NSString stringWithFormat:#"%#",locatedAtcountry]];
//Set the label text to current location
//[locationLabel setText:locatedAt];
}];

Resources