swiftclgeocoderclplacemark

Location Lookup on Keystroke like Apple Weather app


I'd like to perform a location lookup like the Apple Weather app which shows a table of potential locations with each keystroke. I'm sending the user's input string to the function below each time an "editing changed" event occurs in the text field. The string seems to be sent properly, but I'm not getting back the expected array of placemarks. For example "Chestnut" returns only "Chestnut, IL", but if I type "Chestnut Hi" I get four elements: "Marshfield, MA", "Wilbraham, MA", "South Hadley, MA", and "Greenfield, MA". Then typing "Chesnut Hil" returns "Brookline, MA", which wasn't even in the "Chestnut Hi" list. Code is below. Thanks much!

func forwardGeocoding(address: String) {
    CLGeocoder().geocodeAddressString(address, completionHandler: { (placemarks, error) in
        if error != nil {
            print(error!)
            return
        }
        var placeName = ""
        var placeCoordinate = ""
        self.placeNames = [] // empty arrays at the start of each geocode result
        self.placeCoordinates = []

        if (placemarks?.count)! > 0 {

            for placemark in placemarks! {

                if placemark.country != "United States" {
                    let city = placemark.locality ?? ""
                    let country = placemark.country ?? ""
                    placeName = "\(city) \(country)"
                } else {
                    let city = placemark.locality ?? ""
                    placeName = "\(city), \(placemark.administrativeArea!)"
                }

                let coordinate = placemark.location?.coordinate
                placeCoordinate = "\(coordinate!.latitude), \(coordinate!.longitude)"
                self.placeNames.append(placeName)
                self.placeCoordinates.append(placeCoordinate)
            }
        }
        self.tableView.reloadData()
    })
}

Solution

  • Some notable snippets from the CLGeocoder docs, emphasis added:

    A geocoder object is a single-shot object that works with a network-based service to look up placemark information for its specified coordinate value


    Applications should be conscious of how they use geocoding. Geocoding requests are rate-limited for each app, so making too many requests in a short period of time may cause some of the requests to fail.


    The computer or device must have access to the network in order for the geocoder object to return detailed placemark information

    All of this suggests that a) using CLGeocoder to return and refine "as you type" search results is probably not going to work well, in general, and b) therefore it's probably not what Apple's using in Weather.

    Remember that Weather doesn't want to map user-entered strings to lat/longs on the globe — it's searching against a list of place names. (Specifically, the list of places for which Apple's weather forecast partners provide data.) If that's the kind of thing you want to search against, you'll need your own such list.

    If you want "as you type" search results, you're best served by a local database, or at the very least a web service that's tailored to that use. As noted in this old answer, there are plenty of options for that — Google offers some services, and GeoNames.org is a free/open option that has both web services and downloadable databases that you could embed in your app.

    Once you have such a database/service, you'll also want to think about how you use it. For example, Do you want "chestnut hi" to find Chestnut Hill, PA or (hypothetically; there isn't one) Chestnut, Hawaii? How you preprocess search strings and make them into database queries will affect your results.