0

I'm trying to learn Swift, and I have a little project with Google's places API.

I have a method for fetching places details, which uses URLSession in swift to send the request:

func fetchRestaurantDetails(placeId: String) -> Void {
    let jsonURLString = "https://maps.googleapis.com/maps/api/place/details/json?placeid=\(placeId)&key=[MY API KEY]"
    guard let url = URL(string: jsonURLString) else { return}

    let urlRequest = URLRequest(url: url)

    // set up the session
    let config = URLSessionConfiguration.default
    let session = URLSession(configuration: config)


    _ = session.dataTask(with: urlRequest) { (data, response, error) in
        // check for any errors
        guard error == nil else {
            print("error calling GET on /todos/1")
            print(error!)
            return
        }
        // make sure we got data
        guard let responseData = data else {
            print("Error: did not receive data")
            return
        }
        // parse the result as JSON, since that's what the API provides
        do {
            let place = try JSONDecoder().decode(Result.self, from: responseData) // New in Swift 4, used to serialize json.  
            self.rest = place.result
        } catch  {
            print("error trying to convert data to JSON")
            return
        }
    }.resume()
}

I use this method to create a instance of type Restaurants, which I will later add to a list:

func createRestaurant(placeId: String) -> Restaurants {
    self.fetchRestaurantDetails(placeId: placeId)
    let rest = Restaurants(name: self.rest.name,
                           formatted_address: self.rest.formatted_address,
                           website: self.rest.website,
                           location: ((self.rest.geometry.location.lat,self.rest.geometry.location.lng)),
                           opening_hours: self.rest.opening_hours.weekday_text,
                           photo: restImg)
    return rest!
}

But whenever I reach back into the "let rest = Restaurants(...)" all the values are nil. When I try to debug it, it just jumps over my "_ = session" sections right down to resume(), then back to session again and ends back at resume(). No data produced. I'm quite puzzled since I successfully executed this piece of code before, and now I'm wondering if I missed something. Thx :-)

em0605
  • 197
  • 2
  • 9

2 Answers2

1

Put two breakpoints. One at

    let place = try JSONDecoder().decode(Result.self, from: responseData) // New in Swift 4, used to serialize json.  
    self.rest = place.result

and the second one at

let rest = Restaurants(name: self.rest.name,
                       formatted_address: self.rest.formatted_address,
                       website: self.rest.website,
                       location: ((self.rest.geometry.location.lat,self.rest.geometry.location.lng)),
                       opening_hours: self.rest.opening_hours.weekday_text,
                       photo: restImg)

You will realise that the second one is getting called first. You are fetching data, which is done asynchronously, and before its available you are trying to use it. You need to make sure that the data is available before you use it. One way here would be to use completion handler. You can learn about completion handlers here.

Mohammad Sadiq
  • 5,070
  • 28
  • 29
  • Thank you! That was also my own initial feeling, but guess I just could not wrap my head around completion handlers! Thx. – em0605 Jul 25 '17 at 13:14
0

fetchRestaurantDetails is an asynchronous method due to the fact that you call session.dataTask in it, which is asynchronous.

You are trying to use the results of the function before it actually returned. You have several ways to solve this issue:

  1. Use a completion handler to return the value from fetchRestaurantDetails
  2. Use DispatchGroups to detect when the URLRequest finished
  3. Use a 3rd party framework like PromiseKit to handle the asynchronous functions like normal functions with return values.
Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116