Skip to content

Instantly share code, notes, and snippets.

@mshafer
Last active February 13, 2022 03:47
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mshafer/cdae4de0d7b5a13c5765affdb91ddb34 to your computer and use it in GitHub Desktop.
Save mshafer/cdae4de0d7b5a13c5765affdb91ddb34 to your computer and use it in GitHub Desktop.
MKLocalSearchCompleter results in SwiftUI using Combine. More explanation: https://www.mozzafiller.com/posts/mklocalsearchcompleter-swiftui-combine
import Foundation
import SwiftUI
import MapKit
import Combine
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 {}
import SwiftUI
struct MKLocalSearchCompleterExampleView: View {
@ObservedObject var locationSearchService: LocationSearchService
var body: some View {
NavigationView {
VStack {
SearchBar(text: $locationSearchService.searchQuery)
List(locationSearchService.completions) { completion in
VStack(alignment: .leading) {
Text(completion.title)
Text(completion.subtitle)
.font(.subheadline)
.foregroundColor(.gray)
}
}.navigationBarTitle(Text("Search near me"))
}
}
}
}
// Create the SwiftUI view that provides the window contents.
let locationSearchService = LocationSearchService()
let contentView = MKLocalSearchCompleterExampleView(locationSearchService: locationSearchService)
import SwiftUI
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
return searchBar
}
func updateUIView(_ uiView: UISearchBar, context: UIViewRepresentableContext<SearchBar>) {
uiView.text = text
}
}
@mshafer
Copy link
Author

mshafer commented Apr 5, 2020

For more explanation on how this works, see this post: https://www.mozzafiller.com/posts/mklocalsearchcompleter-swiftui-combine

Result

mklocalsearchcompleter-swiftui

@DonBaronFactory
Copy link

Wow, the only usable and easily comprehensive solution out there... thank you for this contribution

@saroar
Copy link

saroar commented Aug 9, 2021

how to get the address + coordinate?

@mshafer
Copy link
Author

mshafer commented Aug 29, 2021

Hi @saroar – the .subtitle property on the MKLocalSearchCompletion objects is usually the address of the result. But to get more details (e.g. coordinates) you need to convert the completion object into an MKLocalSearch. See this StackOverflow answer: How to extract country and city from MKLocalSearchCompletion?

@Mcrich23
Copy link

Whenever I show the search bar in a view, it clears within a couple seconds and the search breaks, any ideas?

@Mcrich23
Copy link

I also get a bunch of errors:

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 more

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment