I have some code below that searches for cities in the world. Only the city name and country is returned. Is there any way I can tweak the code to also get the coordinates of each result? I know I can use a geocoder to get the coordinates but I was hoping there's a simpler way since I'm already using MKLocalSearch.
class CitySearchViewModel: NSObject, ObservableObject, MKLocalSearchCompleterDelegate {
@Published var searchQuery: String = ""
@Published var searchResults: [CityResult] = []
private var searchCompleter: MKLocalSearchCompleter!
override init() {
super.init()
searchCompleter = MKLocalSearchCompleter()
searchCompleter.delegate = self
searchCompleter.resultTypes = .address
}
func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
let results = getCityList(results: completer.results)
let final = Array(Set(results))
DispatchQueue.main.async {
self.searchResults = final
}
}
func completer(_ completer: MKLocalSearchCompleter, didFailWithError error: Error) { }
func performSearch() {
searchCompleter.queryFragment = searchQuery
}
struct CityResult: Hashable {
var city: String
var country: String
}
private func getCityList(results: [MKLocalSearchCompletion]) -> [CityResult] {
var searchResults: [CityResult] = []
for result in results {
let titleComponents = result.title.components(separatedBy: ", ")
let subtitleComponents = result.subtitle.components(separatedBy: ", ")
buildCityTypeA(titleComponents, subtitleComponents) { place in
if !place.city.isEmpty && !place.country.isEmpty {
searchResults.append(CityResult(city: place.city, country: place.country))
}
}
buildCityTypeB(titleComponents, subtitleComponents) { place in
if !place.city.isEmpty && !place.country.isEmpty {
searchResults.append(CityResult(city: place.city, country: place.country))
}
}
}
return searchResults
}
private func buildCityTypeA(_ title: [String], _ subtitle: [String], _ completion: @escaping ((city: String, country: String)) -> Void) {
var city: String = ""
var country: String = ""
if title.count > 1 && subtitle.count >= 1 {
city = title.first!
country = subtitle.count == 1 && subtitle[0] != "" ? subtitle.first! : title.last!
}
completion((city, country))
}
private func buildCityTypeB(_ title: [String], _ subtitle: [String], _ completion: @escaping ((city: String, country: String)) -> Void) {
var city: String = ""
var country: String = ""
if title.count >= 1 && subtitle.count == 1 {
city = title.first!
country = subtitle.last!
}
completion((city, country))
}
}
Thanks to JermeyP I was able to compose this complete solution based on his code. The solution below retrieves cities that match a search string along with their corresponding country and coordinates.