Hi,
I am trying to use mapkit auto completion in my SwiftUI project and whenever I run the view, I get a bunch of errors such as:
2022-02-12 19:45:59.095462-0800 Pickt[64199:2417526] [SearchAttribution] No matching attribution source found for com.timeout
2022-02-12 19:45:59.095595-0800 Pickt[64199:2417526] [SearchAttribution] No matching attribution source found for com.theculturetrip
2022-02-12 19:45:59.095822-0800 Pickt[64199:2417526] [SearchAttribution] No matching attribution source found for com.redtri
2022-02-12 19:45:59.095909-0800 Pickt[64199:2417526] [SearchAttribution] No matching attribution source found for com.fotospot
2022-02-12 19:45:59.096549-0800 Pickt[64199:2417526] [SearchAttribution] Error loading attribution info for identifier com.timeout from geod: Error Domain=GEOErrorDomain Code=-8 "No matching attribution source found for com.timeout" UserInfo={NSDebugDescription=No matching attribution source found for com.timeout}
2022-02-12 19:45:59.096679-0800 Pickt[64199:2417526] [SearchAttribution] Error loading attribution info for identifier com.theculturetrip from geod: Error Domain=GEOErrorDomain Code=-8 "No matching attribution source found for com.theculturetrip" UserInfo={NSDebugDescription=No matching attribution source found for com.theculturetrip}
2022-02-12 19:45:59.096761-0800 Pickt[64199:2417526] [SearchAttribution] Error loading attribution info for identifier com.redtri from geod: Error Domain=GEOErrorDomain Code=-8 "No matching attribution source found for com.redtri" UserInfo={NSDebugDescription=No matching attribution source found for com.redtri}
2022-02-12 19:45:59.096837-0800 Pickt[64199:2417526] [SearchAttribution] Error loading attribution info for identifier com.fotospot from geod: Error Domain=GEOErrorDomain Code=-8 "No matching attribution source found for com.fotospot" UserInfo={NSDebugDescription=No matching attribution source found for com.fotospot}
and then the auto complete breaks until I dismiss and reload the view. Here is my code:
MapAutoComplete.swift
import Foundation
import SwiftUI
import Combine
import MapKit
class MapSearch : NSObject, ObservableObject {
@Published var locationResults : [MKLocalSearchCompletion] = []
@Published var searchTerm = ""
private var cancellables : Set<AnyCancellable> = []
private var searchCompleter = MKLocalSearchCompleter()
private var currentPromise : ((Result<[MKLocalSearchCompletion], Error>) -> Void)?
override init() {
super.init()
searchCompleter.delegate = self
searchCompleter.resultTypes = MKLocalSearchCompleter.ResultType([.address])
$searchTerm
.debounce(for: .seconds(0.2), scheduler: RunLoop.main)
.removeDuplicates()
.flatMap({ (currentSearchTerm) in
self.searchTermToResults(searchTerm: currentSearchTerm)
})
.sink(receiveCompletion: { (completion) in
//handle error
}, receiveValue: { (results) in
self.locationResults = results.filter { $0.subtitle.contains("United States") } // This parses the subtitle to show only results that have United States as the country. You could change this text to be Germany or Brazil and only show results from those countries.
})
// .store(in: &cancellables)
}
func searchTermToResults(searchTerm: String) -> Future<[MKLocalSearchCompletion], Error> {
Future { promise in
self.searchCompleter.queryFragment = searchTerm
self.currentPromise = promise
}
}
}
extension MapSearch : MKLocalSearchCompleterDelegate {
func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
currentPromise?(.success(completer.results))
}
func completer(_ completer: MKLocalSearchCompleter, didFailWithError error: Error) {
//could deal with the error here, but beware that it will finish the Combine publisher stream
//currentPromise?(.failure(error))
}
}
struct ReversedGeoLocation {
let streetNumber: String // eg. 1
let streetName: String // eg. Infinite Loop
let city: String // eg. Cupertino
let state: String // eg. CA
let zipCode: String // eg. 95014
let country: String // eg. United States
let isoCountryCode: String // eg. US
var formattedAddress: String {
return """
\(streetNumber) \(streetName),
\(city), \(state) \(zipCode)
\(country)
"""
}
// Handle optionals as needed
init(with placemark: CLPlacemark) {
self.streetName = placemark.thoroughfare ?? ""
self.streetNumber = placemark.subThoroughfare ?? ""
self.city = placemark.locality ?? ""
self.state = placemark.administrativeArea ?? ""
self.zipCode = placemark.postalCode ?? ""
self.country = placemark.country ?? ""
self.isoCountryCode = placemark.isoCountryCode ?? ""
}
}
struct SearchBar: UIViewRepresentable {
@Binding var text: String
class Coordinator: NSObject, UISearchBarDelegate {
@Binding var text: String
init(text: Binding<String>) {
_text = text
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
text = searchText
}
}
func makeCoordinator() -> SearchBar.Coordinator {
return Coordinator(text: $text)
}
func makeUIView(context: UIViewRepresentableContext<SearchBar>) -> UISearchBar {
let searchBar = UISearchBar(frame: .zero)
searchBar.delegate = context.coordinator
searchBar.searchBarStyle = .minimal
searchBar.becomeFirstResponder()
return searchBar
}
func updateUIView(_ uiView: UISearchBar, context: UIViewRepresentableContext<SearchBar>) {
uiView.text = text
}
}
class LocationSearchService: NSObject, ObservableObject, MKLocalSearchCompleterDelegate {
@Published var searchQuery = ""
var completer: MKLocalSearchCompleter
@Published var completions: [MKLocalSearchCompletion] = []
var cancellable: AnyCancellable?
override init() {
completer = MKLocalSearchCompleter()
super.init()
cancellable = $searchQuery.assign(to: \.queryFragment, on: self.completer)
completer.delegate = self
}
func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
self.completions = completer.results
}
}
extension MKLocalSearchCompletion: Identifiable {}
struct searchView: View {
@ObservedObject var locationSearchService: LocationSearchService
@Environment(\.presentationMode) var presentationMode
static var address = ""
var body: some View {
VStack {
SearchBar(text: $locationSearchService.searchQuery)
List(locationSearchService.completions) { completion in
VStack(alignment: .leading) {
Text(completion.title)
Text(completion.subtitle)
.font(.subheadline)
.foregroundColor(.gray)
}
.onTapGesture {
searchView.address = completion.subtitle
presentationMode.wrappedValue.dismiss()
}
}
}
}
}
I am in desperate need of help, thx in advance!