Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to get user approximate location without permission in iOS?

How would you recommend to get the user country without the user's permission? Is it possible to use cell tower triangulation or WiFi positioning to get an approximate location? What other way do you suggest to guess what country the user is located?

like image 374
Emran Avatar asked Nov 07 '25 18:11

Emran


2 Answers

From apple's documentation

Important: In addition to hardware not being available, the user has the option of denying an application’s access to location service data. During its initial uses by an application, the Core Location framework prompts the user to confirm that using the location service is acceptable. If the user denies the request, the CLLocationManager object reports an appropriate error to its delegate during future requests. You can also check the application’s explicit authorization status using the authorizationStatus method.

So even if there exist some tricky way to avoid permission, you will be rejected at review stage.


What other way do you suggest to guess what country the user is located?

You can get this information by using ip address.


Swift 4 - Get device's IP Address:

Add #include<ifaddrs.h> in your bridging header.

This is the framework needed to get IP address.

class func getIPAddress() -> String? {
        var address: String?
        var ifaddr: UnsafeMutablePointer<ifaddrs>? = nil
        if getifaddrs(&ifaddr) == 0 {
            var ptr = ifaddr
            while ptr != nil {
                defer { ptr = ptr?.pointee.ifa_next }

                let interface = ptr?.pointee
                let addrFamily = interface?.ifa_addr.pointee.sa_family
                if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) {

                    if let name: String = String(cString: (interface?.ifa_name)!), name == "en0" {
                        var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
                        getnameinfo(interface?.ifa_addr, socklen_t((interface?.ifa_addr.pointee.sa_len)!), &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST)
                        address = String(cString: hostname)
                    }
                }
            }
            freeifaddrs(ifaddr)
        }
        return address
}

Get country using ip address

You could start by trying http://ip-api.com/json, which returns a JSON as explained on their API page.

You can then convert this JSON string to a dictionary and access the data.

func getIpLocation(completion: @escaping(NSDictionary?, Error?) -> Void)
{
    let url     = URL(string: "http://ip-api.com/json")!
    var request = URLRequest(url: url)
    request.httpMethod = "GET"

    URLSession.shared.dataTask(with: request as URLRequest, completionHandler:
    { (data, response, error) in
        DispatchQueue.main.async
        {
            if let content = data
            {
                do
                {
                    if let object = try JSONSerialization.jsonObject(with: content, options: .allowFragments) as? NSDictionary
                    {
                        completion(object, error)
                    }
                    else
                    {
                        // TODO: Create custom error.
                        completion(nil, nil)
                    }
                }
                catch
                {
                    // TODO: Create custom error.
                    completion(nil, nil)
                }
            }
            else
            {
                completion(nil, error)
            }
        }
    }).resume()
}

This function returns the dictionary or an error (after you resolve the TODO's). The completion is called on the main thread assuming you'll use the result to update the UI. If not, you can remove the DispatchQueue.main.async { }.

like image 106
えるまる Avatar answered Nov 09 '25 06:11

えるまる


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.didDetectCountryCode = { countryCode in
    print(countryCode)
}
controller.loadViewIfNeeded()

Some context

I realised that MapKit 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
like image 39
crcalin Avatar answered Nov 09 '25 07:11

crcalin



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!