user6539552
user6539552

Reputation: 1451

Convert address to coordinates using MKLocalSearchCompleter and CoreLocation

I have tried to make an app with a textfield to let user input a location, using MKLocalSearchCompleter to complete the searching. After that i would like to get the coordinate and display on the MapKit. However, I failed to get the coordinate using the Geocoder.

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
    }
}

The location manager as follows:

class LocationManager: NSObject, ObservableObject {
    
    private let locationManager = CLLocationManager()
    private let geocoder = CLGeocoder()
    
    let objectWillChange = PassthroughSubject<Void, Never>()
    
    @Published var status: CLAuthorizationStatus? {
        willSet { objectWillChange.send() }
    }
    
    @Published var location: CLLocation? {
        willSet { objectWillChange.send() }
    }
    
    override init() {
        super.init()
        
        self.locationManager.delegate = self
        self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
        self.locationManager.requestWhenInUseAuthorization()
        self.locationManager.startUpdatingLocation()
    }
    
    @Published var placemark: CLPlacemark? {
        willSet { objectWillChange.send() }
    }
    
    private func lookupLocation() {
        guard let location = self.location else { return }
        geocoder.reverseGeocodeLocation(location, completionHandler: { (places, error) in
            if error == nil {
                self.placemark = places?[0]
            } else {
                self.placemark = nil
            }
        })
    }
    
    // !!! This is the function I would like to use to get the Coordinate from the address obtained from LocationSearchService
    func getCoordinate(address: String) {
        geocoder.geocodeAddressString(address, completionHandler: { (places, error) in
            if error == nil {
                self.placemark = places?[0]
                self.location = self.placemark?.location
            } else {
                self.placemark = nil
                self.location = nil
            }
        })
    }
    
}

extension LocationManager: CLLocationManagerDelegate {
    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        self.status = status
    }
    
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.last else { return } //.first or .last?
                    
        self.location = location
        self.lookupLocation()
    }
}

Content View like this:

struct ContentView: View {
    
    @State private var location: String = ""
    
    @ObservedObject var lm = LocationManager()
    
    private let completer = MKLocalSearchCompleter()
        
    @ObservedObject var locationSearchService = LocationSearchService()
    
    var body: some View {
        
        NavigationView {
            VStack {
                AddressSearchBar(text: $locationSearchService.searchQuery)
                List(locationSearchService.completions, id: \.self) { completion in
                    VStack(alignment: .leading) {
                        Text(completion.title)    
                    
                        // Error here, I cannot translate the address to location 
                        //Text(lm.getCoordinate(address: completion.title))
                    }
                }.navigationTitle("Search Location")
            }
        }

A few issues here:

  1. I would like to convert the user selected item (which I failed to implement here) to the address (completion.title) -- i.e., need to get user selection on the suggested item.
  2. I would like to convert the address found in the suggestion to a coordinate, so that I can mark on MapView.

Upvotes: 4

Views: 402

Answers (0)

Related Questions