When I look at apple docs, I get a bit confused.
On one hand they have statements indicating that CLGeocoder can be used from iOS 5 onwards.
On the other hand they have statements like: "For applications running on iOS 4.1 and earlier, you must perform reverse-geocoding requests using the MKReverseGeocoder class"
Then what about the 4.2 onwards range? Can it support CLGeocoder or not?
Does anyone have an answer via their first hand experience with an iPhone with 4.1 < version < 5.0 installed where they figured out if CLGeocoder will work or not?
Currently I call
[[CLGeocoder class] instancesRespondToSelector:#selector(reverseGeocodeLocation:completionHandler:)]
This always returns false on devices below 5.0
I'm still trying to figure out why as the documentation states its supported 4.1 onwards.
Here is quote from the Location Awareness Programming Guide:
In iOS, you can use either the CLGeocoder or MKReverseGeocoder class
to handle reverse-geocoding requests. The CLGeocoder is the preferred
class to use and is available in iOS 5.0 and later. However, if your
application must run on earlier versions of iOS, you can use the
MKReverseGeocoder class.
MKReveresGeocoder is still there in 5.0, but is deprecated. That is, you could still use it to simplify support for multiple iOS versions in your code.
- (IBAction)geoCodeLocation:(id)sender{
[self.geoCoder reverseGeocodeLocation: locationManager.location 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(#"placemark %#",placemark.country); // Give Country Name
NSLog(#"placemark %#",placemark.locality); // Extract the city name
NSLog(#"location %#",placemark.name);
NSLog(#"location %#",placemark.ocean);
NSLog(#"location %#",placemark.postalCode);
NSLog(#"location %#",placemark.subLocality);
NSLog(#"location %#",placemark.location);
//Print the location to console
NSLog(#"I am currently at %#",locatedAt);
//Set the label text to current location
[locationLabel setText:locatedAt];
}];
For more see property of CLPlacemark Link : http://developer.apple.com/library/ios/#DOCUMENTATION/CoreLocation/Reference/CLPlacemark_class/Reference/Reference.html (Support in iOS 5.0 because in previous MKReverseGeocoder is deprecated)
Related
I am trying to get user's region code in swift 3 by using:
Locale.current.regionCode
Unfortunately regionCode is nil, do you have an idea why?
And what should I do to obtain it?
Thanks a lot.
For those looking for solutions at SIMULATOR, go to "Settings > General > Language & Region" and change the region.
It worked for me and other people.
For a weird unknown reason, some simulators doesn't return the region until it changes at least once.
I don't know if it works at real device as well, because I did not had this problem on real device.
I ran into the same issue and as it turns out it was because I set my Scheme to a specific language. If you set both, Application Language & Application Region to "System Language / Region" it should work.
Many of the properties on Locale can return nil for various reasons on iOS (and Android).
You may also use objectForKey:NSLocaleCountryCode to query the country code.
// code may be nil
NSString *code = [[NSLocale currentLocale] objectForKey:NSLocaleCountryCode];
It's a good idea to have a fallback logic to find countryCode with CoreTelephony.
CTCarrier *carrier = [[CTTelephonyNetworkInfo new] subscriberCellularProvider];
NSString *countryCode = carrier.isoCountryCode;
Or/And with reverse geocode:
// CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
__block CLLocation *location = [locations firstObject];
[[[CLGeocoder alloc] init] reverseGeocodeLocation:location completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
if (!error && placemarks.count > 0) {
CLPlacemark *placemark = [placemarks firstObject];
// Use placemark.country;
// Use placemark.ISOCountryCode;
}
}
}
For instance, on Android version of corresponding region query for Locale, it's nil for many of the rooted devices.
The documentation for regionCode states
The region code of the locale, or nil if it has none.
Some locales simply do not have a region (aka country) code. However I don't know which ones do not.
the latest Xcode/SDK iOS download is no longer providing the 'country' string.
- (void)GEOLocator
{
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
CLLocation *myLocation = [[CLLocation alloc] initWithLatitude:currentCentre.latitude longitude:currentCentre.longitude];
[geocoder reverseGeocodeLocation:myLocation completionHandler:^(NSArray *placemarks, NSError *error)
{
// the returned error code is 0
NSLog(#"%ld",(long)error.code);
// there’s only one entry in the placemarks NSArray
NSLog(#"placemarks count(%lu)",(unsigned long)[placemarks count]);
CLPlacemark *placemark = [placemarks firstObject];
// and the country property is null
NSLog(#"placemark.country(%#)",placemark.country);
}
];
}
currentCentre.latitude and currentCentre.longitude is hardcoded to downtown San Francisco: these have been proven to work.
the NSLog output’s are:
2015-08-29 15:47:48.299 MyApp[10128:548448] 0
2015-08-29 15:47:48.299 MyApp[10128:548448] placemarks count(1)
2015-08-29 15:47:48.300 MyApp[10128:548448] placemark.country((null))
this code sequence is about as simple as it gets, yet the latest Xcode/iOS download no longer can tell me what country I’m in?!
if this isn't correct can someone please post the correct way to retrieve 'county' from CLLocation?
the core problem is that in Xcode/Simulator you can set lat/long values in multiple places begging the question: what happens when these settings conflict?
the simulator had me out in the middle of the North Atlantic! which explains why the 'country' (as well as several others) were null. the difficulty lie in setting a test lat/long value and the fact one can do that in multiple places: 1) Edit Scheme, 2) the Simulator Loction menu item, and 3) directly in source.
it would be nice to set test lat/long's in one place and everything then just works.
At the moment I'm working on an application for the iPad, which at a certain point should show a list of cities with next to these cities the distance from the users current location.
I'm using a loop which uses a database I built earlier. This is the code:
NSString *address = [NSString stringWithFormat:#"%#, Nederland", [info2 objectForKey:#"stadsnaam"]];
[self.geocoder geocodeAddressString:address completionHandler:^(NSArray *placemarks, NSError *error) {
CLPlacemark *placemark = [placemarks objectAtIndex:0];
CLLocation *location = placemark.location;
CLLocationCoordinate2D firstLocation = location.coordinate;
NSLog(#"%f,%f", firstLocation.latitude, firstLocation.longitude);
}];
This piece of code is inside a for loop. But the first time it is run, it just returns 0.0000
The second time the loop is activated (and I'm sure it runs more than one time) it returns only the distance between the current location and the FIRST city.
The app gets the city names from this piece of code:[NSString stringWithFormat:#"%#, Nederland", [info2 objectForKey:#"stadsnaam"]]
Which I verified and works. I would like to avoid using the Google API because I will be making more than the maximum request a day probably XD
Any help is appreciated, please inform me if anything isn't completely clear and thanks in advance!
You need to read the documentation for -geocodeAddressString:completionHandler::
After initiating a forward-geocoding request, do not attempt to initiate another forward- or reverse-geocoding request.
You can use CLGeocoder's geocoding property to determine whether a geocoding operation is in progress.
I am trying to Reverse geocode location from Lat/Long value that I get earlier in the App and I would like from this coordinate to find the city name, country name and ISO.
I am currently using CLLocationManager to get actual location information with the folowing code:
//Auto geolocation and find city/country
locationManager.delegate=self;
//Get user location
[locationManager startUpdatingLocation];
[self.geoCoder reverseGeocodeLocation: locationManager.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];
}];
It is working perfectly but, It is possible to do the same from Long/Lat value that I had already saved in the device and not with the current location like on the actual code ?
Update and solution:
Thanks Mark for the answer, I finally use the following code to get info from saved coordinate:
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];
}];
Yes. Create a CLLocation object using the initWithLatitude:longitude: method using your saved lat/lon values, and pass that to reverseGeocodeLocation:.
I am surprised that you say this is working (although, if you're on the simulator, location services are simulated anyway, which might be the reason) because when you call startUpdatingLocation, your implementation of CLLocationManagerDelegate methods like locationManager:didUpdateToLocation:fromLocation: get called. (You've implemented these right?) It is only when this (and other) delegate method is called that you can be certain that you have successfully determined the user's location.
You may want to read up on the CLLocationManagerDelegate protocol and on Location Services best practices as documented by Apple.
I'm developing an iOS app with reverse geocoding features according to this article: geocoding tutorial
But when I test like this on simulator, I get 'kCLErrorDomain error 9'. I've searched a lot and there are only error 0 or 1 not 9.
Here is my code in viewDidLoad:
self.locationManager = [[CLLocationManager alloc]init];
self.locationManager.delegate = self;
self.locationManager.distanceFilter = 80.0;
[self.locationManager startUpdatingLocation];
CLGeocoder *geocoder = [[[CLGeocoder alloc] init] autorelease];
[geocoder reverseGeocodeLocation:self.locationManager.location
completionHandler:^(NSArray *placemarks, NSError *error) {
NSLog(#"reverseGeocodeLocation:completionHandler: Completion Handler called!");
if (error){
NSLog(#"Geocode failed with error: %#", error);
return;
}
if(placemarks && placemarks.count > 0)
{
//do something
CLPlacemark *topResult = [placemarks objectAtIndex:0];
NSString *addressTxt = [NSString stringWithFormat:#"%# %#,%# %#",
[topResult subThoroughfare],[topResult thoroughfare],
[topResult locality], [topResult administrativeArea]];
NSLog(#"%#",addressTxt);
}
}];
Thank you very much.
The Core Location error codes are documented here.
Code values 8, 9, and 10 are:
kCLErrorGeocodeFoundNoResult,
kCLErrorGeocodeFoundPartialResult,
kCLErrorGeocodeCanceled
Based on the code shown, the most likely reason you'd get error 8 is that it's trying to use location immediately after calling startUpdatingLocation at which time it might still be nil.
It usually takes a few seconds to obtain the current location and it will most likely be nil until then (resulting in geocode error 8 or kCLErrorGeocodeFoundNoResult). I'm not sure what error code 9 means by "FoundPartialResult" but Apple's GeocoderDemo sample app treats both the same way (as "No Result").
Try moving the geocoding code (all the code after the startUpdatingLocation call) to the delegate method locationManager:didUpdateToLocation:fromLocation:. The location manager will call that delegate method when it actually has a location and only then is it safe to use location.
There, after the geocoding is successful (or not), you may want to call stopUpdatingLocation otherwise it will try geocoding every time the user location is updated.
You may also want to check the accuracy (newLocation.horizontalAccuracy) and age (newLocation.timestamp) of the received location before trying to geocode it.
It turns out I mixed up the longitute and latitude when creating the location. Thought I'd add this as something for people to check.