I am trying to generate a Formatted Full address using CLGeocoder in Swift 3. I referred to this SO thread to get the code given below.
However, sometimes the app crashe
I create my own static class for Geocoding and get attributes of CLPlacemark and obtain a complete address, like "usually" returns Google:
import Foundation
import CoreLocation
class ReverseGeocoding {
static func geocode(latitude: Double, longitude: Double, completion: @escaping (CLPlacemark?, _ completeAddress: String?, Error?) -> ()) {
CLGeocoder().reverseGeocodeLocation(CLLocation(latitude: latitude, longitude: longitude)) { placemarks, error in
guard let placemark = placemarks?.first, error == nil else {
completion(nil, nil, error)
return
}
let completeAddress = getCompleteAddress(placemarks)
completion(placemark, completeAddress, nil)
}
}
static private func getCompleteAddress(_ placemarks: [CLPlacemark]?) -> String {
guard let placemarks = placemarks else {
return ""
}
let place = placemarks as [CLPlacemark]
if place.count > 0 {
let place = placemarks[0]
var addressString : String = ""
if place.thoroughfare != nil {
addressString = addressString + place.thoroughfare! + ", "
}
if place.subThoroughfare != nil {
addressString = addressString + place.subThoroughfare! + ", "
}
if place.locality != nil {
addressString = addressString + place.locality! + ", "
}
if place.postalCode != nil {
addressString = addressString + place.postalCode! + ", "
}
if place.subAdministrativeArea != nil {
addressString = addressString + place.subAdministrativeArea! + ", "
}
if place.country != nil {
addressString = addressString + place.country!
}
return addressString
}
return ""
}
}
Then the implementation:
ReverseGeocoding.geocode(coordinate: coordinate, completion: { (placeMark, completeAddress, error) in
if let placeMark = placeMark, let completeAddress = completeAddress {
print(placeMark.postalCode)
print(placeMark)
print(completeAddress)
} else {
// do something with the error
}
Finaly the print:
15172
Calle del Arenal, 4, Calle del Arenal, 4, 15172 Oleiros, A Coruña, España @ <+43.33190337,-8.37144380> +/- 100.00m, region CLCircularRegion (identifier:'<+43.33190337,-8.37144380> radius 70.84', center:<+43.33190337,-8.37144380>, radius:70.84m)
Calle del Arenal, 4, Oleiros, 15172, A Coruña, España
CLGeocoder().reverseGeocodeLocation(CLLocation(latitude: vehicleLocation.latitude, longitude: vehicleLocation.latitude), completionHandler: {(placemarks, error) -> Void in
guard error == nil else {completionHandler(nil); return}
guard let place = placemarks else {completionHandler(nil); return}
if place.count > 0 {
let pm = place[0]
var addArray:[String] = []
if let name = pm.name {
addArray.append(name)
}
if let thoroughfare = pm.thoroughfare {
addArray.append(thoroughfare)
}
if let subLocality = pm.subLocality {
addArray.append(subLocality)
}
if let locality = pm.locality {
addArray.append(locality)
}
if let subAdministrativeArea = pm.subAdministrativeArea {
addArray.append(subAdministrativeArea)
}
if let administrativeArea = pm.administrativeArea {
addArray.append(administrativeArea)
}
if let country = pm.country {
addArray.append(country)
}
if let postalCode = pm.postalCode {
addArray.append(postalCode)
}
let addressString = addArray.joined(separator: ",\n")
print(addressString)
completionHandler(addressString)
}
else { completionHandler(nil)}
})
func convertLatLongToAddress(latitude:Double, longitude:Double) {
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: latitude, longitude: longitude)
var labelText = ""
geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
var placeMark: CLPlacemark!
placeMark = placemarks?[0]
if placeMark != nil {
if let name = placeMark.name {
labelText = name
}
if let subThoroughfare = placeMark.subThoroughfare {
if (subThoroughfare != placeMark.name) && (labelText != subThoroughfare) {
labelText = (labelText != "") ? labelText + "," + subThoroughfare : subThoroughfare
}
}
if let subLocality = placeMark.subLocality {
if (subLocality != placeMark.subThoroughfare) && (labelText != subLocality) {
labelText = (labelText != "") ? labelText + "," + subLocality : subLocality
}
}
if let street = placeMark.thoroughfare {
if (street != placeMark.subLocality) && (labelText != street) {
labelText = (labelText != "") ? labelText + "," + street : street
}
}
if let locality = placeMark.locality {
if (locality != placeMark.thoroughfare) && (labelText != locality) {
labelText = (labelText != "") ? labelText + "," + locality : locality
}
}
if let city = placeMark.subAdministrativeArea {
if (city != placeMark.locality) && (labelText != city) {
labelText = (labelText != "") ? labelText + "," + city : city
}
}
if let state = placeMark.postalAddress?.state {
if (state != placeMark.subAdministrativeArea) && (labelText != state) {
labelText = (labelText != "") ? labelText + "," + state : state
}
}
if let country = placeMark.country {
labelText = (labelText != "") ? labelText + "," + country : country
}
// labelText gives you the address of the place
}
})
}
Here as an improvement I added place name as well. It makes address more meaningful.
Keeping it simple - A full Swift 3 & 4 compatible View Controller example for obtaining a formatted address string from user's location (add in the other keys available in CLPlacemark if you want more information in your string):
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
let manager = CLLocationManager()
let geocoder = CLGeocoder()
var locality = ""
var administrativeArea = ""
var country = ""
override func viewDidLoad() {
super.viewDidLoad()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations[0]
manager.stopUpdatingLocation()
geocoder.reverseGeocodeLocation(location, completionHandler: {(placemarks, error) in
if (error != nil) {
print("Error in reverseGeocode")
}
let placemark = placemarks! as [CLPlacemark]
if placemark.count > 0 {
let placemark = placemarks![0]
self.locality = placemark.locality!
self.administrativeArea = placemark.administrativeArea!
self.country = placemark.country!
}
})
}
func userLocationString() -> String {
let userLocationString = "\(locality), \(administrativeArea), \(country)"
return userLocationString
}
}
Calling print(userLocationString()) in this example will print: suburb, state, country
Don't forget to add Privacy - Location When In Use Usage Description to your Info.plist file beforehand, to allow the user to grant permissions to your app to utilise location services.
Here's a 2-3 line version of the answers here:
func getAddress(placemarks: [CLPlacemark]) -> String {
guard let placemark = placemarks.first, !placemarks.isEmpty else {return ""}
let outputString = [placemark.locality,
placemark.subLocality,
placemark.thoroughfare,
placemark.postalCode,
placemark.subThoroughfare,
placemark.country].compactMap{$0}.joined(separator: ", ")
print(outputString)
return outputString
}
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)
}
})
}