Find city name and country from latitude and longitude in Swift

I’m working on application in Swift3
and I have letter problem i can’t find the answer for it.

How can I know city name and country short names base on latitude and longitude?

  • iPhone : HOWTO move status bar with pan gesture
  • Search as you type Swift
  • How to get next 10 days from current date in swift
  • QRCode Scanner Not Working in iOS 11, only showing camera preview
  • How can I pop specific View Controller in Swift
  • Open UISplitViewController to Master View rather than Detail
  • import UIKit
    import CoreLocation
    
    class ViewController: UIViewController, CLLocationManagerDelegate{
        let locationManager = CLLocationManager()
        var latitude: Double = 0
        var longitude: Double = 0
        override func viewDidLoad() {
            super.viewDidLoad()
            // For use when the app is open & in the background
            locationManager.requestAlwaysAuthorization()
            // For use when the app is open
            //locationManager.requestWhenInUseAuthorization()
            locationManager.delegate = self
            locationManager.startUpdatingLocation()
            if CLLocationManager.locationServicesEnabled() {
                locationManager.delegate = self
                locationManager.desiredAccuracy = kCLLocationAccuracyBest
                locationManager.startUpdatingLocation()
            }
        }
        func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
            if let location = locations.first {
                print(location.coordinate)
                latitude = location.coordinate.latitude
                longitude = location.coordinate.longitude
            }
        }
        func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
            if (status == CLAuthorizationStatus.denied){
                showLocationDisabledpopUp()
            }
        }
        func showLocationDisabledpopUp() {
            let alertController = UIAlertController(title: "Background Location Access  Disabled", message: "We need your location", preferredStyle: .alert)
            let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
            alertController.addAction(cancelAction)
            let openAction = UIAlertAction(title: "Open Setting", style: .default) { (action) in
                if let url = URL(string: UIApplicationOpenSettingsURLString){
                    UIApplication.shared.open(url, options: [:], completionHandler: nil)
                }
            }
            alertController.addAction(openAction)
            self.present(alertController, animated: true, completion: nil)
        }
    }
    

    4 Solutions Collect From Internet About “Find city name and country from latitude and longitude in Swift”

    I would recommend integrating Google Maps API with your project. If you do, your task can be achieved using Reverse Geocoding Google provides.

    Furthermore, Google there is Google Maps SDK for IOS development, which is also worth considering.

    UPD: You can do that without integrating maps into your project. Basing on this answer, you can achieve that using http requests to Google API. The request to:

    https://maps.googleapis.com/maps/api/geocode/json?latlng=40.714224,-73.961452&key=API_KEY 
    

    would return JSON object with information about the requested place, including country and city name.

    BTW, I highly recommend using Alamofire to make http requests in Swift.

    You can use CLGeocoder, from CoreLocation, for that. From Apple documentation (emphasizes mine):

    A single-shot object for converting between geographic coordinates and place names.

    The CLGeocoder class provides services for converting between a coordinate (specified as a latitude and longitude) and the user-friendly representation of that coordinate. A user-friendly representation of the coordinate typically consists of the street, city, state, and country information corresponding to the given location…

    This service is unrelated to MapKit and, as such, don’t require you use/show a map in your app at all.

    What you need is called reverse geocoding. As you have already declared some properties at the top. You need to add the CLGeocoder & CLPlancemark

    let locationManager = CLLocationManager()
    var location: CLLocation?
    
    let geocoder = CLGeocoder()
    var placemark: CLPlacemark?
    
    // here I am declaring the iVars for city and country to access them later
    
    var city: String?
    var country: String?
    var countryShortName: String?
    

    Create a function where you can start the location services

    func startLocationManager() {
        // always good habit to check if locationServicesEnabled
        if CLLocationManager.locationServicesEnabled() {
            locationManager.delegate = self
            locationManager.desiredAccuracy = kCLLocationAccuracyBest
            locationManager.startUpdatingLocation()
        }
    }
    

    also create another to stop once you’re done with location geocoding

    func stopLocationManager() {
       locationManager.stopUpdatingLocation()
       locationManager.delegate = nil
    }
    

    in view didLoad or from anywhere you want to start the location manager add a check first

    override func viewDidLoad() {
    super.viewDidLoad()
    
        let authStatus = CLLocationManager.authorizationStatus()
        if authStatus == .notDetermined {
            locationManager.requestWhenInUseAuthorization()
        }
    
        if authStatus == .denied || authStatus == .restricted {
            // add any alert or inform the user to to enable location services 
        }
    
       // here you can call the start location function
       startLocationManager()
    
    }
    

    implement the delegate methods for location manager didFailedWithError

    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        // print the error to see what went wrong
        print("didFailwithError\(error)")
        // stop location manager if failed
        stopLocationManager()
    }
    

    implement the delegate method for location manager didUpdateLocations

     func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        // if you need to get latest data you can get locations.last to check it if the device has been moved
        let latestLocation = locations.last!
    
        // here check if no need to continue just return still in the same place
        if latestLocation.horizontalAccuracy < 0 {
            return
        }
        // if it location is nil or it has been moved
        if location == nil || location!.horizontalAccuracy > lastLocation.horizontalAccuracy {
    
            location = lastLocation
            // stop location manager
            stopLocationManager()
    
            // Here is the place you want to start reverseGeocoding
            geocoder.reverseGeocodeLocation(lastLocation, completionHandler: { (placemarks, error) in
                    // always good to check if no error
                    // also we have to unwrap the placemark because it's optional
                    // I have done all in a single if but you check them separately 
                    if error == nil, let placemark = placemarks, !placemark.isEmpty {
                        self.placemark = placemark.last
                    }
                    // a new function where you start to parse placemarks to get the information you need
                    self.parsePlacemarks()
    
               })
        }
    }
    

    Add the parsePlacemarks function

    parsePlacemarks() {
       // here we check if location manager is not nil using a _ wild card 
       if let _ = location {
            // unwrap the placemark 
            if let placemark = placemark {
                // wow now you can get the city name. remember that apple refers to city name as locality not city
                // again we have to unwrap the locality remember optionalllls also some times there is no text so we check that it should not be empty
                if let city = placemark.locality, !city.isEmpty {
                    // here you have the city name
                    // assign city name to our iVar
                    self.city = city
                }
                // the same story optionalllls also they are not empty
                if let country = placemark.country, !country.isEmpty {
    
                    self.country = country
                }
                // get the country short name which is called isoCountryCode
                if let countryShortName = placemark.isoCountryCode, !countryShortName.isEmpty {
    
                    self.countryShortName = countryShortName
                }
    
            }
    
    
        } else {
           // add some more check's if for some reason location manager is nil
        }
    
    }
    

    You have to cmd+click on CLPlacemark to see all the properties that you can access for example street name is called thoroughfare & the number is is called subThoroughfare continue reading the documentation for more information

    Note: You have to check for locations error also geocoder error which I haven’t implemented here but you have to take care of those errors and the best place to check error codes and everything else is apples documentation

    Update: Check paresPlacemarks function where I added isoCountryCode which is equal to country shortName No need to add extra network calls to google API and Alamofire while your already using location services

    You can use CLGeocoder reverseGeocodeLocation method to fetch a CLPlacemark and get is country and locality properties info. Note that it is an asynchronous method so you will need to add a completion handler to your method when fetching that info:

    func fetchCountryAndCity(location: CLLocation, completion: @escaping (String, String) -> ()) {
        CLGeocoder().reverseGeocodeLocation(location) { placemarks, error in
            if let error = error {
                print(error)
            } else if let country = placemarks?.first?.country,
                let city = placemarks?.first?.locality {
                completion(country, city)
            }
        }
    }
    

    Usage

    let location = CLLocation(latitude: -22.963451, longitude: -43.198242)
    
    fetchCountryAndCity(location: location) { country, city in
        print("country:", country)
        print("city:", city)
    }