Reputation: 81
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
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.
Upvotes: 2