Swift - Generate an Address Format from Reverse Geocoding

This is my code for swift 3

func getAdressName(coords: CLLocation) {

    CLGeocoder().reverseGeocodeLocation(coords) { (placemark, error) in
            if error != nil {
                print("Hay un error")
            } else {

                let place = placemark! as [CLPlacemark]
                if place.count > 0 {
                    let place = placemark![0]
                    var adressString : String = ""
                    if place.thoroughfare != nil {
                        adressString = adressString + place.thoroughfare! + ", "
                    }
                    if place.subThoroughfare != nil {
                        adressString = adressString + place.subThoroughfare! + "\n"
                    }
                    if place.locality != nil {
                        adressString = adressString + place.locality! + " - "
                    }
                    if place.postalCode != nil {
                        adressString = adressString + place.postalCode! + "\n"
                    }
                    if place.subAdministrativeArea != nil {
                        adressString = adressString + place.subAdministrativeArea! + " - "
                    }
                    if place.country != nil {
                        adressString = adressString + place.country!
                    }

                    self.lblPlace.text = adressString
                }
            }
        }
  }

You can esaily call above funcation like:

let cityCoords = CLLocation(latitude: newLat, longitude: newLon)
cityData(coord: cityCoords)

  1. For fixing the empty address issue, either you can use a class property to hold the appended value or you can use a closure to return the value back to the calling function
  2. For fixing the crash you need to avoid the force unwrapping of optionals

Using a closure you can do it like:

// Using closure
func getAddress(handler: @escaping (String) -> Void)
{
    var address: String = ""
    let geoCoder = CLGeocoder()
    let location = CLLocation(latitude: selectedLat, longitude: selectedLon)
    //selectedLat and selectedLon are double values set by the app in a previous process
    
    geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
        
        // Place details
        var placeMark: CLPlacemark?
        placeMark = placemarks?[0]
        
        // Address dictionary
        //print(placeMark.addressDictionary ?? "")
        
        // Location name
        if let locationName = placeMark?.addressDictionary?["Name"] as? String {
            address += locationName + ", "
        }
        
        // Street address
        if let street = placeMark?.addressDictionary?["Thoroughfare"] as? String {
            address += street + ", "
        }
        
        // City
        if let city = placeMark?.addressDictionary?["City"] as? String {
            address += city + ", "
        }
        
        // Zip code
        if let zip = placeMark?.addressDictionary?["ZIP"] as? String {
            address += zip + ", "
        }
        
        // Country
        if let country = placeMark?.addressDictionary?["Country"] as? String {
            address += country
        }
        
       // Passing address back
       handler(address)
    })
}

You can call the method like:

getAddress { (address) in
    print(address)
}

Formatting addresses is hard because each country has its own format.

With a few lines of code, you can get the correct address format for each country and let Apple handle the differences.

Since iOS 11, you can get a Contacts framework address:

extension CLPlacemark {
    @available(iOS 11.0, *)
    open var postalAddress: CNPostalAddress? { get }
}

This extension is part of the Contacts framework. This means, this feature is invisible to you in the XCode code completion until you do

import Contacts

With this additional import, you can do something like

CLGeocoder().reverseGeocodeLocation(location, preferredLocale: nil) { (clPlacemark: [CLPlacemark]?, error: Error?) in
    guard let place = clPlacemark?.first else {
        print("No placemark from Apple: \(String(describing: error))")
        return
    }

    let postalAddressFormatter = CNPostalAddressFormatter()
    postalAddressFormatter.style = .mailingAddress
    var addressString: String?
    if let postalAddress = place.postalAddress {
        addressString = postalAddressFormatter.string(from: postalAddress)
    }
}

and get the address formatted in the format for the country in the address.

The formatter even supports formatting as an attributedString.

Prior to iOS 11, you can convert CLPlacemark to CNPostalAddress yourself and still can use the country specific formatting of CNPostalAddressFormatter.


func getAddressFromLatLon(pdblLatitude: String, withLongitude pdblLongitude: String) {
        var center : CLLocationCoordinate2D = CLLocationCoordinate2D()
        let lat: Double = Double("\(pdblLatitude)")!
        //21.228124
        let lon: Double = Double("\(pdblLongitude)")!
        //72.833770
        let ceo: CLGeocoder = CLGeocoder()
        center.latitude = lat
        center.longitude = lon

        let loc: CLLocation = CLLocation(latitude:center.latitude, longitude: center.longitude)


        ceo.reverseGeocodeLocation(loc, completionHandler:
            {(placemarks, error) in
                if (error != nil)
                {
                    print("reverse geodcode fail: \(error!.localizedDescription)")
                }
                let pm = placemarks! as [CLPlacemark]

                if pm.count > 0 {
                    let pm = placemarks![0]
                    print(pm.country)
                    print(pm.locality)
                    print(pm.subLocality)
                    print(pm.thoroughfare)
                    print(pm.postalCode)
                    print(pm.subThoroughfare)
                    var addressString : String = ""
                    if pm.subLocality != nil {
                        addressString = addressString + pm.subLocality! + ", "
                    }
                    if pm.thoroughfare != nil {
                        addressString = addressString + pm.thoroughfare! + ", "
                    }
                    if pm.locality != nil {
                        addressString = addressString + pm.locality! + ", "
                    }
                    if pm.country != nil {
                        addressString = addressString + pm.country! + ", "
                    }
                    if pm.postalCode != nil {
                        addressString = addressString + pm.postalCode! + " "
                    }


                    print(addressString)
              }
        })

    }