title: How to request Place Details from Google Places API using place_id in Swift?
body: <p>Im creating an app that needs to be able to determine what locations are in a given area and then retrieve details about those locations, which then will be displayed. To do this I've been using Google Places API, but I am having trouble retrieving place details for locations using their place_id.</p>
  
  <p>At the moment I am able to successfully perform a Google Places 'Nearby Search' request which returns many locations and some basic data about them (including a place_id). What I'm having trouble with is using the place-id's I receive from the first request to make a 'Place Details' request to get all the information Google has about the respective location.</p>
  
  <p>vv My code below details my current attempt vv</p>
  
  <p>function where I call the 2 requests:</p>
  
  <pre><code>    func requestAndCombineGData(location: CLLocation, radius: Int) {
  
          self.mapView.clear()
  
          // Calls 'Nearby Search' request
          googleClient.getGooglePlacesData(location: location, withinMeters: radius) { (response) in
              print("Made Nearby Search request. Passed response here:", response)
  
              // loops through each result from the 'Nearby Request' to get the 'place_id' and make 'Place Details'
              for location in response.results {
                  // Calls 'Place Details' request
                  self.googleClient.getGooglePlacesDetailsData(place_id: location.place_id) { (detailsResponse) in
                      print("Made Place Details request. Passed response here:", detailsResponse)
  
  //                    function to drop markers using data received above here
  //                    self.putPlaces(places: response.results)
                  }
              }
  
          }
  
      }
  
  </code></pre>
  
  <p><strong>GoogleClient.swift</strong> file that contains the code for handling the requests above:</p>
  
  <pre><code>import SwiftUI
  import Foundation
  import CoreLocation
  
  
  //Protocol
  protocol GoogleClientRequest {
      var googlePlacesKey : String { get set }
      func getGooglePlacesData(location: CLLocation, withinMeters radius: Int, using completionHandler: @escaping (GooglePlacesResponse) -&gt; ())
      func getGooglePlacesDetailsData(place_id: String, using completionHandler: @escaping (GooglePlacesDetailsResponse) -&gt; ())
  }
  
  
  
  // GoogleClient class that conforms to the ZGoogleClient Request protocol
  class GoogleClient: GoogleClientRequest {
  
      let session = URLSession(configuration: .default)
      var googlePlacesKey: String = "MY_KEY_GOES_HERE"
      let categoriesArray = [
          "park",
          "restaurant",
          "zoo"
      ]
  
  
      func getGooglePlacesData(location: CLLocation, withinMeters radius: Int, using completionHandler: @escaping (GooglePlacesResponse) -&gt; ())  {
  
          for category in categoriesArray {
  
              let url = googlePlacesNearbyDataURL(forKey: googlePlacesKey, location: location, radius: radius, type: category)
  
              let task = session.dataTask(with: url) { (responseData, _, error) in
                  if let error = error {
                      print(error.localizedDescription)
                      return
                  }
  
                  guard let data = responseData, let response = try? JSONDecoder().decode(GooglePlacesResponse.self, from: data) else {
                      print("Could not decode JSON response")
                      completionHandler(GooglePlacesResponse(results:[]))
                      return
                  }
  
  
                  if response.results.isEmpty {
                      print("GC - response returned empty", response)
                  } else {
                      print("GC - response contained content", response)
                      completionHandler(response)
                  }
  
              }
              task.resume()
          }
  
      }
  
  
      func getGooglePlacesDetailsData(place_id: String, using completionHandler: @escaping (GooglePlacesDetailsResponse) -&gt; ())  {
  
          let url = googlePlacesDetailsURL(forKey: googlePlacesKey, place_ID: place_id)
  
          let task = session.dataTask(with: url) { (responseData, _, error) in
              if let error = error {
                  print(error.localizedDescription)
                  return
              }
  
              guard let data = responseData, let detailsResponse = try? JSONDecoder().decode(GooglePlacesDetailsResponse.self, from: data) else {
                  print("Could not decode JSON response. responseData was: ", responseData)
                  completionHandler(GooglePlacesDetailsResponse(results:[]))
                  return
              }
  
  //                print("response result: ", detailsResponse.results)
  
              if detailsResponse.results.isEmpty {
                  print("getGPDetails - response returned empty", detailsResponse)
              } else {
                  print("getGPDetails - response contained content", detailsResponse)
                  completionHandler(detailsResponse)
              }
  
          }
          task.resume()
  
      }
  
  
      func googlePlacesNearbyDataURL(forKey apiKey: String, location: CLLocation, radius: Int, type: String) -&gt; URL {
          print("passed  location  before url creation ", location)
          print("passed  radius  before url creation ", radius)
  
          let baseURL = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?"
          let locationString = "location=" + String(location.coordinate.latitude) + "," + String(location.coordinate.longitude)
          let radiusString = "radius=" + String(radius)
          let typeString = "type=" + String(type)
  //        let rankby = "rankby=distance"
  //        let keywrd = "keyword=" + keyword
          let key = "key=" + apiKey
  
          print("Request URL:", URL(string: baseURL + locationString + "&amp;" + radiusString + "&amp;" + type + "&amp;" + key)!)
          return URL(string: baseURL + locationString + "&amp;" + radiusString + "&amp;" + typeString + "&amp;" + key)!
      }
  
  
      func googlePlacesDetailsURL(forKey apiKey: String, place_ID: String) -&gt; URL {
          print("passed  place_ID  before url creation ", place_ID)
  
          let baseURL = "https://maps.googleapis.com/maps/api/place/details/json?"
          let place_idString = "place_id=" + place_ID
          let fields = "fields=rating"
          let key = "key=" + apiKey
  
          print("Details request URL:", URL(string: baseURL + place_idString + "&amp;" + fields + "&amp;" + key)!)
          return URL(string: baseURL + place_idString + "&amp;" + fields + "&amp;" + key)!
      }
  
  
  
  }
  
  </code></pre>
  
  <p><strong>ResponseModels.swift</strong> file that holds the structs to handle the 2 different request responses:</p>
  
  <pre><code>import SwiftUI
  import Foundation
  
  
  
  struct GooglePlacesResponse : Codable {
      let results : [Place]
      enum CodingKeys : String, CodingKey {
          case results = "results"
      }
  }
  
  
  
  // Place struct
  struct Place : Codable {
  
      let geometry : Location
      let name : String
      let place_id: String
      let openingHours : OpenNow?
      let photos : [PhotoInfo]?
      let types : [String]
      let address : String
  
  
      enum CodingKeys : String, CodingKey {
          case geometry = "geometry"
          case name = "name"
          case place_id = "place_id"
          case openingHours = "opening_hours"
          case photos = "photos"
          case types = "types"
          case address = "vicinity"
      }
  
  
      // Location struct
      struct Location : Codable {
  
          let location : LatLong
  
          enum CodingKeys : String, CodingKey {
              case location = "location"
          }
  
  
          // LatLong struct
          struct LatLong : Codable {
  
              let latitude : Double
              let longitude : Double
  
              enum CodingKeys : String, CodingKey {
                  case latitude = "lat"
                  case longitude = "lng"
              }
          }
  
      }
  
  
      // OpenNow struct
      struct OpenNow : Codable {
  
          let isOpen : Bool
  
          enum CodingKeys : String, CodingKey {
              case isOpen = "open_now"
          }
      }
  
  
      // PhotoInfo struct
      struct PhotoInfo : Codable {
  
          let height : Int
          let width : Int
          let photoReference : String
  
          enum CodingKeys : String, CodingKey {
              case height = "height"
              case width = "width"
              case photoReference = "photo_reference"
          }
      }
  
  
  
  }
  
  
  
  struct GooglePlacesDetailsResponse : Codable {
      let results : [PlaceDetails]
      enum CodingKeysDetails : String, CodingKey {
          case results = "results"
      }
  }
  
  
  
  // PlaceDetails struct
  // I have fields commented out because I wanted to just get a location's rating for testing before implementing the rest
  struct PlaceDetails : Codable {
  
  //    let place_id: String
  //    let geometry : Location
  //    let name : String
      let rating : CGFloat?
  //    let price_level : Int
  //    let types : [String]
  //    let openingHours : OpenNow?
  //    let formatted_address : String
  //    let formatted_phone_number : String
  //    let website : String
  //    let reviews : String
  //    let photos : [PhotoInfo]?
  
  
      enum CodingKeysDetails : String, CodingKey {
  //        case place_id = "place_id"
  //        case geometry = "geometry"
  //        case name = "name"
          case rating = "rating"
  //        case price_level = "price_level"
  //        case types = "types"
  //        case openingHours = "opening_hours"
  //        case formatted_address = "formatted_address"
  //        case formatted_phone_number = "formatted_phone_number"
  //        case website = "website"
  //        case reviews = "reviews"
  //        case photos = "photos"
      }
  
  
  
  }
  
  
  </code></pre>
  
  <p>Google Places API documentation I've been referencing:</p>
  
  <pre><code>https://developers.google.com/places/web-service/search
  </code></pre>
  
  <p>-</p>
  
  <p>Like I said above, right now my first request ('Nearby Search') is successfully returning data, but when I try to implement the second request ('Place Details') using a 'place_id'. My console keeps returning "Could not decode JSON response. Response:  Optional(106 bytes)" which comes from a comment in my 'GoogleClient.swift' file.</p>
  
  <p>My questions are:</p>
  
  <p><strong>What is wrong with how I'm making the 'Place Details' request that is causing it to return that the response cant be decoded?</strong></p>
  
  <p>AND</p>
  
  <p><strong>Is there a better way to make a Nearby Search request then use the 'place_id' from the returned locations to make a Place Details request in swift?</strong></p>