Search code examples
iosobjective-cswiftlocation

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?


Solution

  • 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 { }.