Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to extract street, city, etc. from GMSPlace Address Components

I'm using Google Places API for iOS and can successfully retrieve nearby places and present the address as a string. What I'm trying to do is extract address components such as city to store in a database.

The documentation indicates a GMSPlace has an addressComponents property (an array), but I can't seem to figure how to use this property.

The code below provides sets the entire address to the text of a label, but I can't get beyond that:

Edit ---- added code that shows how I'm trying to access Address Components

venueLabel.isHidden = false
            venueLabel.text = selectedPlace?.name
            addressLabel.isHidden = false
            addressLabel.text = selectedPlace?.formattedAddress
            let addressComponents = selectedPlace?.addressComponents
            for component in addressComponents! {
                let city = component["city"] //Error Type GMSPaceAddressComponent has no subscript members
                print(city)
            }
like image 323
Martin Muldoon Avatar asked May 22 '17 12:05

Martin Muldoon


3 Answers

It's working in Swift 5 and iOS 13.3

1. Create a function for showing GMSAutocompleteViewController

    func googlePlacesSearchVC() {
    
        let acController = GMSAutocompleteViewController()
        acController.delegate = self
        // Specify the place data types to return.
        let fields: GMSPlaceField = GMSPlaceField(rawValue: UInt(GMSPlaceField.name.rawValue) |
      UInt(GMSPlaceField.formattedAddress.rawValue)  |
               UInt(GMSPlaceField.addressComponents.rawValue))!
    
        acController.placeFields = fields
        // Specify a filter.
        let filter = GMSAutocompleteFilter()
        filter.country = "IN"
        acController.autocompleteFilter = filter

        acController.secondaryTextColor = .darkGray
        acController.primaryTextColor = .lightGray
        acController.primaryTextHighlightColor = .black
        acController.tableCellBackgroundColor = .whiteThree
        acController.tableCellSeparatorColor = .lightGray
        // Display the autocomplete view controller.
        present(acController, animated: true) {

            let views = acController.view.subviews
            let subviewsOfSubview = views.first!.subviews
            let subOfNavTransitionView = subviewsOfSubview[1].subviews
            let subOfContentView = subOfNavTransitionView[1].subviews
            let searchBar = subOfContentView[0] as! UISearchBar
            searchBar.searchTextField.placeholder = "places_picker_hint_add_address".localized
            searchBar.searchBarStyle = .minimal
            searchBar.searchTextField.font = UIFont.screenTitle16Pt
            searchBar.searchTextField.backgroundColor = UIColor.white
            searchBar.searchTextField.leftView?.tintColor = .darkGray
            searchBar.delegate?.searchBar?(searchBar, textDidChange: "")
        }
   }

2. Call that function where ever you need, for ex:-

   override func viewDidLoad() {
      super.viewDidLoad()

      googlePlacesSearchVC()
   }

3. Call GMSAutoCompleteViewControllerDelegates Methods. Here will get all details like Street, City, etc. from GMSPlace Address Components

extension ViewController {

      // Handle the user's selection.
      func viewController(_ viewController: GMSAutocompleteViewController, didAutocompleteWith place: GMSPlace) {
        
            // Show HouseAndFlat
            if place.name?.description != nil {
                
                yourTxtField.text = place.name?.description ?? ""
            }

            // Show latitude
            if place.coordinate.latitude.description.count != 0 {
                var latitude = place.coordinate.latitude
            }
            // Show longitude
            if place.coordinate.longitude.description.count != 0 {
                selectedLongitude = place.coordinate.longitude
            }

            // Show AddressComponents
            if place.addressComponents != nil {
                
                for addressComponent in (place.addressComponents)! {
                   for type in (addressComponent.types){

                       switch(type){
                           case "sublocality_level_2":
                               yourTxtField.text = addressComponent.name
                           case "sublocality_level_1":
                                yourTxtField.text = addressComponent.name
                            case "administrative_area_level_2":
                                yourTxtField.text = addressComponent.name

                            case "administrative_area_level_1":
                                 yourTxtField.text = addressComponent.name
                            case "country":
                                yourTxtField.text = addressComponent.name

                            case "postal_code":
                                 yourTxtField.text = addressComponent.name
                       default:
                           break
                       }
                   }
               }
            }
            dismiss(animated: true, completion: nil)
      }

      func viewController(_ viewController: GMSAutocompleteViewController, didFailAutocompleteWithError error: Error) {
            // TODO: handle the error.
            print("Error: ", error.localizedDescription)
      }

      // User canceled the operation.
      func wasCancelled(_ viewController: GMSAutocompleteViewController) {
            dismiss(animated: true, completion: nil)
      }
}

I have found an answer from this link

like image 200
Sreekanth G Avatar answered Oct 07 '22 14:10

Sreekanth G


A safe Swift 4.2 solution:

let name = place.addressComponents?.first(where: { $0.type == "city" })?.name

selectedPlace.addressComponents is a array of GMSAddressComponent which have 2 properties type and name. In you case it will be like

    for component in addressComponents! {
    if component.type == "city" {
    print(component.name)
    }
   }

GMSAddressComponent is a class not a dictionary or array that's you are getting this error.

Additional component types can be referred from the link.

like image 17
Surjeet Rajput Avatar answered Oct 09 '22 18:10

Surjeet Rajput


Swift 4

    var keys = [String]()
    place.addressComponents?.forEach{keys.append($0.type)}
    var values = [String]()
    place.addressComponents?.forEach({ (component) in
        keys.forEach{ component.type == $0 ? values.append(component.name): nil}
    })
    print(values) 
like image 4
Giang Avatar answered Oct 09 '22 19:10

Giang