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.
Related
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.
I'm creating a global app and one of the initial screens will detect which country the user is in. I'm using reverseGeocodeLocation for this.
[geocoder reverseGeocodeLocation:[locations lastObject] completionHandler:^(NSArray *placemarks, NSError *error)
{
CLPlacemark *placemark = [placemarks lastObject];
country = placemark.country;
[locationManager stopUpdatingLocation];
if (country != NULL) {
[self showAlert];
}
else {
[self showActionSheet];
}
}];
When in the UK placemark.country will return "United Kingdom". I'm trying to find a list of the return strings for each country. I couldn't find anything on the apple documentation.
You could write code in 2 minutes to get the list for yourself, iterate through the list of ISO country codes and then convert them from an ISO code to the country name and NSLog each one.
The name displayed would of course only be the name for the current language locale setting on your device, in your case English.
What I want to accomplish in my app is to get the current user location and display it onscreen in a UILabel. I would like to have an NSString of current user's location with a format similar to this: #"City, State/Country". It would be a one-time operation at the start of the app launch.
I have no prior experience with location in iOS and I would like to get some advice on this one - I'm sure it's quite a simple task.
The process is as follows:
Add CoreLocation.framework to your project. See Linking to a Library or a Framework. If you want to use the address book constants that I use below, you might want to add the AddressBook.framework to your project, too.
Start location services. For this purpose, the "significant change" service (less accurate, but lower power consumption) is probably sufficient for city-level accuracy.
When the location manager informs you of the user's location, then perform a reverse geocode of that location.
Stop location services.
Thus, that might look like:
#import <CoreLocation/CoreLocation.h>
#import <AddressBook/AddressBook.h>
#interface ViewController () <CLLocationManagerDelegate>
#property (nonatomic, strong) CLLocationManager *locationManager;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self startSignificantChangeUpdates];
}
- (void)startSignificantChangeUpdates
{
if ([CLLocationManager locationServicesEnabled])
{
if (!self.locationManager)
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
[self.locationManager startMonitoringSignificantLocationChanges];
}
}
- (void)stopSignificantChangesUpdates
{
[self.locationManager stopUpdatingLocation];
self.locationManager = nil;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *location = [locations lastObject];
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) {
CLPlacemark *placemark = placemarks[0];
NSDictionary *addressDictionary = [placemark addressDictionary];
NSString *city = addressDictionary[(NSString *)kABPersonAddressCityKey];
NSString *state = addressDictionary[(NSString *)kABPersonAddressStateKey];
NSString *country = placemark.country;
self.label.text = [NSString stringWithFormat:#"%#, %#, %#", city, state, country];
}];
[self stopSignificantChangesUpdates];
}
Note, the location manager's notification of the location is contingent upon the user electing to share that with your app and will happen, even in the best case scenario, asynchronously. Likewise the reverse geocode happens asynchronously.
See Getting User Location from the Location Awareness Programming Guide.
Use -reverseGeocodeLocation:completionHandler: of CLGeocoder.
Try this code snippet, the only trick is that the CLPlacemark (see the Documentation for available info) you get back from the Geocoder has a bunch of info which isn't always consistent, this was one of my tries from an older project, trying to test for location, street name etc... test with your usage case to find a good match:
- (void)getLocationStringForCoordinates:(CLLocationCoordinate2D)coordinates {
if ( CLLocationCoordinate2DIsValid(coordinates) ) {
CLLocation *photoLocation = [[CLLocation alloc] initWithLatitude:coordinates.latitude longitude:coordinates.longitude];
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:photoLocation
completionHandler:^(NSArray *placemarks, NSError *error) {
CLPlacemark *locationPlacemark = [placemarks lastObject];
// Location (popular name, street, area)
NSString *location = locationPlacemark.subLocality ? locationPlacemark.subLocality : (locationPlacemark.name ? locationPlacemark.name : locationPlacemark.thoroughfare);
// sometimes the location can be the same
// as the city name (for small villages), if so
// make sure location is nil to skip it
// else if
// the location name is not being used but is very short 9less then 20 letters, use that instead
if([locationPlacemark.name isEqualToString:locationPlacemark.locality] && [location isEqualToString:locationPlacemark.name])
location = #"";
else if ( ![locationPlacemark.name isEqualToString:location] && locationPlacemark.name.length < 20 )
location = locationPlacemark.name;
// city
NSString *city = locationPlacemark.subAdministrativeArea ? locationPlacemark.subAdministrativeArea : locationPlacemark.locality;
city = city.length > 0 ? [#", " stringByAppendingString:city] : city;
NSString *locationName = [NSString stringWithFormat:#"%#%#", location, city];
}];
}
}
I've found a really nice and simple to follow tutorial on this topic - http://www.appcoda.com/how-to-get-current-location-iphone-user/
Hope it will be helpful to others!
Take a look at the reverseGeocodeLocation:completionHandler: method for CLGeocoder:
http://developer.apple.com/library/ios/#documentation/CoreLocation/Reference/CLGeocoder_class/Reference/Reference.html#//apple_ref/doc/uid/TP40009573
First you will have to use a CLLocationManager to get a CLLocation representing the user's current position.
I uses CLGeocoder to decode the CLLocation from longitude/latitude to place names. It works fine. But there is still one thing bothers me. When i set the device language to English, the result of the code below:
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation{
/* We received the new location */
NSLog(#"Latitude = %f", newLocation.coordinate.latitude);
NSLog(#"Longitude = %f", newLocation.coordinate.longitude);
[self.geoCoder reverseGeocodeLocation:newLocation completionHandler:^(NSArray* placemarks, NSError* error){
MKPlacemark *placemarker = [placemarks objectAtIndex:0];
NSLog(#"%#",placemarker.locality);
}];
[self.locationManager stopUpdatingLocation];
}
is displayed in english, like : chengdu.
when i change the device language to Chinese,
placemarker.locality
returns a Chinese character value.
But what i really want is that it will always return an English character value (no Chinese character value).
I guess this is something related to the locale. Can anyone help on this? Thanks.
Usually, it is not a good practice to mess with user locales. If the device language is set to Chinese is because the user want to read Chinese characters so, why do you want to show him in English when he already told you that he want Chinese?
Anyway, if for any reason you need to force english, you can trick the geoCoder which uses the standardUserDefaults first language so you can do something like this just before calling the geoCoder method:
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:#"en", nil] forKey:#"AppleLanguages"];
This way, geoCoder will give you all the information in english.
However, this will change the user preferences so it is a best approach to give them back to where they were:
NSMutableArray *userDefaultLanguages = [[NSUserDefaults standardUserDefaults] objectForKey:#"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:#"en", nil] forKey:#"AppleLanguages"];
[self.geoCoder reverseGeocodeLocation:newLocation completionHandler:^(NSArray* placemarks, NSError* error){
MKPlacemark *placemarker = [placemarks objectAtIndex:0];
NSLog(#"%#",placemarker.locality);
}];
[[NSUserDefaults standardUserDefaults] setObject:userDefaultLanguages forKey:#"AppleLanguages"];
As I said, you should really think why you need this, but if you really need this, that would work.
I found a nice solution
NSString *country = placemark.ISOcountryCode;
This will return the exact country no matter your locale is. For example country will be #"US" instead of #"United States"
From ios 11 you can pass a preferredLocale parameter to geocoder reverseGeocodeLocation method.
In Swift:
geocoder.reverseGeocodeLocation(
location: CLLocation,
preferredLocale: Locale?,
completionHandler: {}
)
A preferredLocale value example:
Locale(identifier: "en_US")
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)