LastMove
LastMove

Reputation: 2482

IOS Get User current country reliably

In my iOS App, I need to obtain the user current country.

I know that I can use CLLocation to retrieve the location and then reverseGeocodeLocation to get the country, but it will not work if the user doesn't authorize my app to get his location. I could maybe use (Locale.current as NSLocale).object(forKey: NSLocale.Key.countryCode); but I wonder if the countryCode change when the user travels or only when he manually change his settings?

Is it possible to retrieve this information from the current carrier operator ?

Upvotes: 1

Views: 601

Answers (2)

crcalin
crcalin

Reputation: 1049

I managed to get the country without asking for location permissions using the following approach:

import MapKit

class CountryDectectorViewController: UIViewController {
    var didDetectCountryCode: ((String?) -> Void)?

    override func viewDidLoad() {
        super.viewDidLoad()
        // Map view setup
        let mapView = MKMapView()
        view.addSubview(mapView)
        mapView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            mapView.topAnchor.constraint(equalTo: view.topAnchor),
            mapView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            mapView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            mapView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
        ])
        mapView.layoutIfNeeded()
        // Reverse geocoding map region center
        let location = CLLocation(
            latitude: mapView.region.center.latitude,
            longitude: mapView.region.center.longitude
        )
        CLGeocoder().reverseGeocodeLocation(location) { placemarks, _ in
            self.didDetectCountryCode?(placemarks?.first?.isoCountryCode)
        }
    }
}

To ISO Country Code can be then obtained using:

let controller = CountryDectectorViewController()
controller.loadViewIfNeeded()
controller.didDetectCountryCode = { countryCode in
    print(countryCode)
}

Some context

I realised that UIKit has this information already because everytime the MKMapView is shown, the region is automatically set to fit the current user's country. Using this hypothesis I needed to find a solution to load the map without presenting it and then to reverse geocode the center coordinates to identify the country.

I implemented this solution taking into consideration the following limitations I found:

  • Don't ask for user permissions ideally
  • Device locale was not considered a reliable alternative
  • Detecting the sim carrier actually returns the original carrier country and not the current connected carrier (via roaming)
  • Retrieving the country by IP can be easily faked using VPNs which are becoming more popular lately

Upvotes: 0

Anshad Rasheed
Anshad Rasheed

Reputation: 2566

You can find the country code using carrier by using this

CTTelephonyNetworkInfo *netInfo = [[CTTelephonyNetworkInfo alloc] init];
CTCarrier *carrier = [netInfo subscriberCellularProvider];
NSString *mcc = [carrier mobileCountryCode];

Upvotes: 5

Related Questions