user245935
user245935

Reputation: 81

Get geo location from city name

I wrote code to display City Names when typing in a Text field. I can display the search results with a For Each loop. I need to click on one of the results and get the geo location of the cities name. Now I am struggling with getting geo location (latitude, longitude) from that city names. Is there a solution for that to implement it into my code?

class LocationService: NSObject, ObservableObject {

enum LocationStatus: Equatable {
    case idle
    case noResults
    case isSearching
    case error(String)
    case result
}

@Published var queryFragment: String = ""
@Published private(set) var status: LocationStatus = .idle
@Published private(set) var searchResults: [MKLocalSearchCompletion] = []

private var queryCancellable: AnyCancellable?
private let searchCompleter: MKLocalSearchCompleter!

init(searchCompleter: MKLocalSearchCompleter = MKLocalSearchCompleter()) {
    self.searchCompleter = searchCompleter
    super.init()
    self.searchCompleter.delegate = self

    queryCancellable = $queryFragment
        .receive(on: DispatchQueue.main)
        // we're debouncing the search, because the search completer is rate limited.
        // feel free to play with the proper value here
        .debounce(for: .milliseconds(250), scheduler: RunLoop.main, options: nil)
        .sink(receiveValue: { fragment in
            self.status = .isSearching
            if !fragment.isEmpty {
                self.searchCompleter.queryFragment = fragment
            } else {
                self.status = .idle
                self.searchResults = []
            }
    })
}


extension LocationService: MKLocalSearchCompleterDelegate {
func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {

    // out a lot of places and only shows cities and countries.
    self.searchResults = completer.results.filter({ $0.subtitle == "" })
    self.status = completer.results.isEmpty ? .noResults : .result
}

func completer(_ completer: MKLocalSearchCompleter, didFailWithError error: Error) {
    self.status = .error(error.localizedDescription)
}

}

Upvotes: 1

Views: 450

Answers (1)

Abizern
Abizern

Reputation: 150565

The CoreLocation framework gives you CLGeocoder which can try to translate between coordinates and place names in both directions.

You can try this in a Playground to see how it works:

import Foundation
import CoreLocation

func getCoordinate( addressString : String,
                    completionHandler: @escaping(CLLocationCoordinate2D, NSError?) -> Void ) {
    let geocoder = CLGeocoder()
    geocoder.geocodeAddressString(addressString) { (placemarks, error) in
        if error == nil {
            if let placemark = placemarks?[0] {
                let location = placemark.location!

                completionHandler(location.coordinate, nil)
                return
            }
        }

        completionHandler(kCLLocationCoordinate2DInvalid, error as NSError?)
    }
}

let valid = "New Orleans, Louisiana, USA"
let invalid = "The Moon"

getCoordinate(addressString: valid) { location, error in
    guard error == nil else {print("Error:"); return}

    print(location)
}

getCoordinate(addressString: invalid) { location, error in
    guard error == nil else {print("Error:"); return}

    print(location)
}

The results are returned asynchronously, so you'll need to provide a completion handler.

enter image description here

Upvotes: 2

Related Questions