I am building an uber-like app that shows recommended addresses as the user types. Initially, when I started building the app it prompted for the user's location and the autocomplete was smart because it would search for local addresses first. I realized that having the user's location was unnecessary because this is more of a ride request service than a real-time service. After removing the user's location the autocomplete became very poor (expectedly). This app will be used in a very specific region and I want the autocomplete to be smart. Is there any way to set a region for MKLocalSearch without knowing the users location?
Here is the class that retrieves autocomplete addresses...
import Foundation
import MapKit
class LocationSearchViewModel: NSObject, ObservableObject{
@Published var totalPrice:String = "Loading..."
@Published var results = [MKLocalSearchCompletion]()
@Published var fromResult = MKLocalSearchCompletion()
@Published var toResult = MKLocalSearchCompletion()
@Published var fromAveLocation : AveLocation?
@Published var toAveLocation : AveLocation?
private let searchCompleter = MKLocalSearchCompleter()
@Published var fromQueryFragment: String = ""{
didSet{
searchCompleter.queryFragment = fromQueryFragment
}
}
@Published var toQueryFragment: String = ""{
didSet{
searchCompleter.queryFragment = toQueryFragment
}
}
override init() {
super.init()
searchCompleter.delegate = self
searchCompleter.queryFragment = fromQueryFragment
}
func selectLocation(_ localSearch: MKLocalSearchCompletion,_ localSearch2: MKLocalSearchCompletion){
locationSearch(forLocalSearchCompletion: localSearch){response, error in
self.locationSearch(forLocalSearchCompletion: localSearch2){response2, error2 in
if let error = error{
print("DEBUG: Location search failed with error \(error.localizedDescription)")
return
}
if let error2 = error2{
print("DEBUG: Location search failed with error \(error2.localizedDescription)")
return
}
guard let item = response?.mapItems.first else{return}
guard let item2 = response2?.mapItems.first else{return}
let coordinate = item.placemark.coordinate
let coordinate2 = item2.placemark.coordinate
self.fromAveLocation = AveLocation(title: localSearch.title, subtitle: localSearch.subtitle, coordinate: coordinate)
self.toAveLocation = AveLocation(title: localSearch2.title, subtitle: localSearch2.subtitle, coordinate: coordinate2)
}
}
}
func locationSearch(forLocalSearchCompletion localSearch: MKLocalSearchCompletion,
completion: @escaping MKLocalSearch.CompletionHandler){
let searchRequest = MKLocalSearch.Request()
searchRequest.naturalLanguageQuery = localSearch.title.appending(localSearch.subtitle)
let search = MKLocalSearch(request: searchRequest)
search.start(completionHandler: completion)
}
}
extension LocationSearchViewModel: MKLocalSearchCompleterDelegate{
func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
self.results = completer.results
}
}
Here is where it is invoked...
ScrollView{
VStack(alignment: .leading){
ForEach(viewModel.results, id: \.self){ result in
LocationSearchResultCell(title: result.title, subtitle: result.subtitle)
.onTapGesture {
switch whereIsUser {
case .from:
viewModel.fromQueryFragment = result.title
viewModel.fromResult = result
whereIsUser = .to
focusedField = .to
break
case .lookingFrom:
viewModel.fromQueryFragment = result.title
viewModel.fromResult = result
whereIsUser = .to
focusedField = .to
break
case .to:
viewModel.toQueryFragment = result.title
viewModel.toResult = result
whereIsUser = .done
withAnimation(.spring()){
mapState = .locationSelected
viewModel.selectLocation(viewModel.fromResult, viewModel.toResult)
}
break
case .lookingTo:
viewModel.toQueryFragment = result.title
viewModel.toResult = result
whereIsUser = .done
withAnimation(.spring()){
mapState = .locationSelected
viewModel.selectLocation(viewModel.fromResult, viewModel.toResult)
}
break
default:
break
}
}
}
}
}
checked to make sure the simulator location was set to the right spot. Just in case the MKLocalSearch works even without getting the users location.
You can manually create a region using MKCoordinateRegion(center: CLLocationCoordinate2D, latitudinalMeters: CLLocationDistance, longitudinalMeters: CLLocationDistance)
searchRequest.region = MKCoordinateRegion(center: .init(latitude: /*region latitude*/, longitude: \*region longitude*/), latitudinalMeters: 500, longitudinalMeters: 500)
and/or
searchCompleter.region = MKCoordinateRegion(center: .init(latitude: /*region latitude*/, longitude: \*region longitude*/), latitudinalMeters: 500, longitudinalMeters: 500)