I\'m attempting to write a simple method that\'s fed CLLocationDegrees and returns a CLPlacemark. Looking at Apple\'s documentation, it seems like
There are so many questions dealing with 'reverseGeocodeLocation' on Stack Overflow, I've seen so many of them. I'm using Swift 4.2 and I thought my 'reverseGeocodeLocation' requests might be timing out. Well, this is NOT the case, when starting to move with an iPhone the 'didUpdateLocations' cycles need to mature. It can take two or more cycles before 'reverseGeocodeLocation' returns placemarks.
I wrote a simple app that uses 'reverseGeocodeLocation' until 'placemarks' are returned after repeated 'didUpdateLocations' cycles. This is my own home grown 'SampleCode'. I learned a lot from using the app, it might help others understand how reverse geocode requests work in the real world.
Please give your comments on possible improvements. This code is freely given and freely taken.
import Foundation
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
@IBAction func RefreshLocationButton(_ sender: Any) {
self.requestingPlacemark = true
self.placemarkData = nil
//^Make another Placemark Request
self.requestCounter = 0
self.RequestCounterLabel.text = ""
self.LocationLabel.text = ""
self.PlacemarkLabel.text = ""}
@IBOutlet weak var LocationCounterLabel: UILabel!
@IBOutlet weak var RequestCounterLabel: UILabel!
@IBOutlet weak var LocationLabel: UILabel!
@IBOutlet weak var PlacemarkLabel: UILabel!
let locationManager = CLLocationManager()
var placemarkData: CLPlacemark!
var placemarkString: String!
var printPlacemarkData: Bool!
var didUpdateLocationsCounter: Int = 0
var requestCounter: Int = 0
var requestingPlacemark: Bool = true
override func viewDidLoad() {
super.viewDidLoad()
if CLLocationManager.locationServicesEnabled() {
locationManager.requestAlwaysAuthorization()
locationManager.delegate = self
//locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
locationManager.startUpdatingLocation()
self.RequestCounterLabel.text = ""
self.LocationLabel.text = ""
self.PlacemarkLabel.text = ""}}
func printPlacemarks() {
if printPlacemarkData {
self.placemarkString = "Placemark Data:"
self.placemarkString = buildPlacemarkString(item: 1)
self.placemarkString = buildPlacemarkString(item: 2)
self.placemarkString = buildPlacemarkString(item: 3)
self.placemarkString = buildPlacemarkString(item: 4)
self.placemarkString = buildPlacemarkString(item: 5)
self.placemarkString = buildPlacemarkString(item: 6)
self.placemarkString = buildPlacemarkString(item: 7)
self.placemarkString = buildPlacemarkString(item: 8)
self.placemarkString = buildPlacemarkString(item: 9)
self.placemarkString = buildPlacemarkString(item: 10)
self.PlacemarkLabel.text = self.placemarkString
self.printPlacemarkData = false}}
func buildPlacemarkString(item: Int) -> String {
var elementText: String!
var newString: String!
switch item {
case 1: elementText = "name: " + self.placemarkData.name!
case 2: elementText = "subThoroughfare: " + self.placemarkData.subThoroughfare!
case 3: elementText = "thoroughfare: " + self.placemarkData.thoroughfare!
case 4: elementText = "postalCode: " + self.placemarkData.postalCode!
case 5: elementText = "subLocality: " + self.placemarkData.subLocality!
case 6: elementText = "locality: " + self.placemarkData.locality!
case 7: elementText = "subAdministrativeArea: " + self.placemarkData.subAdministrativeArea!
case 8: elementText = "administrativeArea: " + self.placemarkData.administrativeArea!
case 9: elementText = "country: " + self.placemarkData.country!
case 10: elementText = "isoCountryCode: " + self.placemarkData.isoCountryCode!
default: print("Error: incorrect item number!")}
newString = self.placemarkString + "\n" + elementText
return newString
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
//location prints one time and 'didUpdateLocations' is stopped:
self.didUpdateLocationsCounter = self.didUpdateLocationsCounter + 1
let labelString = String(describing: self.didUpdateLocationsCounter)
self.LocationCounterLabel.text = "Location Update Counter: " + labelString
if self.placemarkData == nil {
//location variable:
self.requestCounter = self.requestCounter + 1
let textString = String(describing: self.requestCounter)
self.RequestCounterLabel.text = "Request Counter: " + textString
if locations.count == 0 {
self.LocationLabel.text = "Locations: There was NONE"}
else {
if locations.count > 0 {
let coordinate2D: CLLocation = locations.first!
let location = CLLocation(latitude: coordinate2D.coordinate.latitude, longitude: coordinate2D.coordinate.longitude)
let printString = String(describing: location)
self.LocationLabel.text = "Location: " + printString
let geocoder: CLGeocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(location, completionHandler: {(placemarks, error) -> Void in
if error != nil {
let errorString = String(describing: error?.localizedDescription)
print("reverse geodcode fail: \(errorString)")
self.LocationCounterLabel.text = ""
self.RequestCounterLabel.text = ""
self.LocationLabel.text = "Reverse Geodcode fail: \(errorString)"
self.PlacemarkLabel.text = ""
self.requestingPlacemark = false
return}
else {
let pm = placemarks! as [CLPlacemark]
//There is ALWAYS 'placemarks' Data
if pm.count > 0 {
self.placemarkData = placemarks![0]
self.printPlacemarkData = true
self.printPlacemarks()
self.requestingPlacemark = false}}})}
else {
if self.requestingPlacemark {
self.LocationLabel.text = "Problem: There is no 'location.first'"}}}}}}
And then the Storyboard UI:
Storyboard Image
View Controller Image