Obtener la ubicación del dispositivo (solo el país) en iOS

Necesito obtener la ubicación del país de un dispositivo iOS.

He estado tratando de usar CoreLocation con MKReverseGeocoder. Sin embargo, esto parece volverse errático con bastante frecuencia. Y solo necesito el país, no hay necesidad de calles y tal.

¿Cómo puede hacerse esto de una manera más estable?

NSLocale es solo una configuración sobre la configuración regional utilizada actualmente, no significa el país real en el que se encuentra.

Utilice CLLocationManager para obtener la ubicación actual y CLGeocoder para realizar geoencoding inversa. Puede obtener el nombre del país desde allí.

 NSString *countryCode = [[NSLocale currentLocale] objectForKey: NSLocaleCountryCode]; 

obtendrá un identificador como, por ejemplo, “EE. UU.” (Estados Unidos), “ES” (España), etc.


En Swift 3 :

 let countryCode = NSLocale.current.regionCode 

En Swift 2.2 :

 let countryCode = NSLocale.currentLocale().objectForKey(NSLocaleCountryCode) as String 

En comparación con una solución basada en CLLocationManager, este enfoque tiene ventajas y desventajas. El problema principal es que no garantiza que esté donde el dispositivo está físicamente si el usuario lo configura de manera diferente. Sin embargo, esto también puede verse como un profesional, ya que en su lugar muestra el país con el que un usuario está mentalmente / culturalmente alineado, así que si, por ejemplo, voy al extranjero de vacaciones, entonces la configuración regional aún se establece en mi país de origen. Sin embargo, un gran pro es que esta API no requiere permiso del usuario, como lo hace CLLocationManager. Entonces, si todavía no has obtenido permiso para usar la ubicación del usuario y no puedes justificar lanzar un diálogo emergente en la cara del usuario (o ellos ya rechazaron esa ventana emergente y necesitas un respaldo) entonces esta es probablemente la API que quiere usar. Algunos casos de uso típicos para esto podrían ser la personalización (por ejemplo, contenido culturalmente relevante, formatos predeterminados, etc.) y análisis.

La respuesta de @Denis es buena: aquí hay un código que pone su respuesta en práctica. Esto es para una clase personalizada que ha configurado para cumplir con el protocolo CLLocationManagerDelegate . Está un poco simplificado (por ejemplo, si el administrador de la ubicación devuelve varias ubicaciones, simplemente va con la primera), pero debería darles un comienzo decente …

 - (id) init //designated initializer { if (self) { self.locationManager = [[CLLocationManager alloc] init]; self.geocoder = [[CLGeocoder alloc] init]; self.locationManager.delegate = self; [self.locationManager startMonitoringSignificantLocationChanges]; } return self; } - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { if (locations == nil) return; self.currentLocation = [locations objectAtIndex:0]; [self.geocoder reverseGeocodeLocation:self.currentLocation completionHandler:^(NSArray *placemarks, NSError *error) { if (placemarks == nil) return; self.currentLocPlacemark = [placemarks objectAtIndex:0]; NSLog(@"Current country: %@", [self.currentLocPlacemark country]); NSLog(@"Current country code: %@", [self.currentLocPlacemark ISOcountryCode]); }]; } 

Aquí están las respuestas de @ Denis y @ Matt juntas para una solución Swift 3 :

 import UIKit import CoreLocation class ViewController: UIViewController, CLLocationManagerDelegate { let locationManager = CLLocationManager() let geoCoder = CLGeocoder() override func viewDidLoad() { super.viewDidLoad() locationManager.requestAlwaysAuthorization() if CLLocationManager.locationServicesEnabled() { locationManager.delegate = self locationManager.startMonitoringSignificantLocationChanges() } } func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { guard let currentLocation = locations.first else { return } geoCoder.reverseGeocodeLocation(currentLocation) { (placemarks, error) in guard let currentLocPlacemark = placemarks?.first else { return } print(currentLocPlacemark.country ?? "No country found") print(currentLocPlacemark.isoCountryCode ?? "No country code found") } } } 

¡No olvide configurar NSLocationAlwaysUsageDescription o NSLocationWhenInUseUsageDescription en Info.plist también!

Aquí hay un método alternativo, quizás excesivamente tortuoso. Las otras soluciones se basan en configuraciones manuales (NSLocale) o al solicitar permiso para usar servicios de ubicación que pueden denegarse (CLLocationManager), por lo que tienen inconvenientes.

Puede obtener el país actual según la zona horaria local. Mi aplicación está interactuando con un servidor que ejecuta Python con pytz instalado, y ese módulo proporciona un diccionario de códigos de país para cadenas de zonas horarias. Solo necesito que el servidor conozca el país, así que no tengo que configurarlo completamente en iOS. En el lado de Python:

 >>> import pytz >>> for country, timezones in pytz.country_timezones.items(): ... print country, timezones ... BD ['Asia/Dhaka'] BE ['Europe/Brussels'] BF ['Africa/Ouagadougou'] BG ['Europe/Sofia'] BA ['Europe/Sarajevo'] BB ['America/Barbados'] WF ['Pacific/Wallis'] ... 

En el lado de iOS:

 NSTimeZone *tz = [NSTimeZone localTimeZone]; DLog(@"Local timezone: %@", tz.name); // prints "America/Los_Angeles" 

Mi servidor envía el nombre de la zona horaria local y lo busca en el diccionario pytz country_timezones.

Si hace que una versión de iOS del diccionario esté disponible en pytz u otra fuente, puede usarla para buscar inmediatamente el código de país sin la ayuda de un servidor, según la configuración de la zona horaria, que a menudo está actualizada.

Sin embargo, puedo estar malinterpretando NSLocale. ¿Le proporciona el código de país a través de las preferencias regionales de formato o la configuración de la zona horaria? Si esto último, entonces esta es solo una forma más complicada de obtener el mismo resultado …

 NSLocale *countryLocale = [NSLocale currentLocale]; NSString *countryCode = [countryLocale objectForKey:NSLocaleCountryCode]; NSString *country = [countryLocale displayNameForKey:NSLocaleCountryCode value:countryCode]; NSLog(@"Country Locale:%@ Code:%@ Name:%@", countryLocale, countryCode, country); //Country Locale:<__nscflocale: 0x7fd4b343ed40> Code:US Name:United States 

Para Swift 3 es aún más simple:

 let countryCode = Locale.current.regionCode 

Puede obtener NSTimeZone desde CLLocation: https://github.com/Alterplay/APTimeZones y funciona localmente.

Si solo está interesado en dispositivos telefónicos, entonces la técnica que se menciona aquí podría serle útil: Determine el país del usuario de iPhone.

Aquí hay un ciclo rápido en Swift 3 que devuelve una lista completa de códigos de país.

 let countryCode = NSLocale.isoCountryCodes for country in countryCode { print(country) } 

@Robar

 let locale = Locale.current let code = (locale as NSLocale).object(forKey: NSLocale.Key.countryCode) as! String? 

Con estos códigos obtendrá el código de país de su región y, si no se quedó quieto, cámbielo simplemente en Configuración del teléfono-> general-> idioma y región, y configure la región que desea.