URL Session Download Task Completion Block Never Called












0















I am making a web request to the Google Places API to fetch nearby places based off a specific coordinate and thereafter run a task to fetch a photo of those places.



I am able to successfully get place details through the first task, and while the second task of retrieving the photo runs, its completion block is never entered resulting in no photo as I have observed placing breakpoints.



The urlString is correct, my apiKey works correctly since again the first task returns the right response, and I have the photo reference of the place to be able to execute the web request. Any guidance would be appreciated as I am admittedly lost - can provide more detail if needed.



Below are the network calls, which is adapted from ray wenderlich's guide.



typealias PlacesCompletion = ([Place]) -> Void
typealias PhotoCompletion = (UIImage?) -> Void

class DataProvider {
private var photoCache: [String: UIImage] = [:]
private var placesTask: URLSessionDataTask?
private var session: URLSession {
return URLSession.shared
}

//FIRST WEB REQUEST
func fetchPlacesNearCoordinate(_ coordinate: CLLocationCoordinate2D, radius: Double, types: [String], completion: @escaping PlacesCompletion) -> Void {
var urlString = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=(coordinate.latitude),(coordinate.longitude)&radius=(radius)&rankby=prominence&sensor=true&key=(googleApiKey)"
let typesString = "restaurant"
urlString += "&type=(typesString)"
urlString = urlString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) ?? urlString

guard let url = URL(string: urlString) else {
completion()
return
}

if let task = placesTask, task.taskIdentifier > 0 && task.state == .running {
task.cancel()
}

DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}

placesTask = session.dataTask(with: url) { data, response, error in
var placesArray: [Place] =
defer {
DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
completion(placesArray)
}
}
guard let data = data,
let json = try? JSON(data: data, options: .mutableContainers),
let results = json["results"].arrayObject as? [[String: Any]] else {
return
}
results.forEach {
let place = Place(dictionary: $0, acceptedTypes: types)
placesArray.append(place)

if let reference = place.photoReference {
//SECOND WEB REQUEST CALL
self.fetchPhotoFromReference(reference) { image in
place.photo = image
}
}
}
}
placesTask?.resume()
}



//SECOND WEB REQUEST
func fetchPhotoFromReference(_ reference: String, completion: @escaping PhotoCompletion) -> Void {
if let photo = photoCache[reference] {
completion(photo)
} else {
let urlString = "https://maps.googleapis.com/maps/api/place/photo?maxwidth=200&maxheight=200&photoreference=(reference)&key=(googleApiKey)"
guard let url = URL(string: urlString) else {
completion(nil)
return
}

DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}

session.downloadTask(with: url) { url, response, error in
var downloadedPhoto: UIImage? = nil
defer {
DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
completion(downloadedPhoto)
}
}
guard let url = url else {
return
}
guard let imageData = try? Data(contentsOf: url) else {
return
}
downloadedPhoto = UIImage(data: imageData)
self.photoCache[reference] = downloadedPhoto
}
.resume()
}
}
}









share|improve this question



























    0















    I am making a web request to the Google Places API to fetch nearby places based off a specific coordinate and thereafter run a task to fetch a photo of those places.



    I am able to successfully get place details through the first task, and while the second task of retrieving the photo runs, its completion block is never entered resulting in no photo as I have observed placing breakpoints.



    The urlString is correct, my apiKey works correctly since again the first task returns the right response, and I have the photo reference of the place to be able to execute the web request. Any guidance would be appreciated as I am admittedly lost - can provide more detail if needed.



    Below are the network calls, which is adapted from ray wenderlich's guide.



    typealias PlacesCompletion = ([Place]) -> Void
    typealias PhotoCompletion = (UIImage?) -> Void

    class DataProvider {
    private var photoCache: [String: UIImage] = [:]
    private var placesTask: URLSessionDataTask?
    private var session: URLSession {
    return URLSession.shared
    }

    //FIRST WEB REQUEST
    func fetchPlacesNearCoordinate(_ coordinate: CLLocationCoordinate2D, radius: Double, types: [String], completion: @escaping PlacesCompletion) -> Void {
    var urlString = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=(coordinate.latitude),(coordinate.longitude)&radius=(radius)&rankby=prominence&sensor=true&key=(googleApiKey)"
    let typesString = "restaurant"
    urlString += "&type=(typesString)"
    urlString = urlString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) ?? urlString

    guard let url = URL(string: urlString) else {
    completion()
    return
    }

    if let task = placesTask, task.taskIdentifier > 0 && task.state == .running {
    task.cancel()
    }

    DispatchQueue.main.async {
    UIApplication.shared.isNetworkActivityIndicatorVisible = true
    }

    placesTask = session.dataTask(with: url) { data, response, error in
    var placesArray: [Place] =
    defer {
    DispatchQueue.main.async {
    UIApplication.shared.isNetworkActivityIndicatorVisible = false
    completion(placesArray)
    }
    }
    guard let data = data,
    let json = try? JSON(data: data, options: .mutableContainers),
    let results = json["results"].arrayObject as? [[String: Any]] else {
    return
    }
    results.forEach {
    let place = Place(dictionary: $0, acceptedTypes: types)
    placesArray.append(place)

    if let reference = place.photoReference {
    //SECOND WEB REQUEST CALL
    self.fetchPhotoFromReference(reference) { image in
    place.photo = image
    }
    }
    }
    }
    placesTask?.resume()
    }



    //SECOND WEB REQUEST
    func fetchPhotoFromReference(_ reference: String, completion: @escaping PhotoCompletion) -> Void {
    if let photo = photoCache[reference] {
    completion(photo)
    } else {
    let urlString = "https://maps.googleapis.com/maps/api/place/photo?maxwidth=200&maxheight=200&photoreference=(reference)&key=(googleApiKey)"
    guard let url = URL(string: urlString) else {
    completion(nil)
    return
    }

    DispatchQueue.main.async {
    UIApplication.shared.isNetworkActivityIndicatorVisible = true
    }

    session.downloadTask(with: url) { url, response, error in
    var downloadedPhoto: UIImage? = nil
    defer {
    DispatchQueue.main.async {
    UIApplication.shared.isNetworkActivityIndicatorVisible = false
    completion(downloadedPhoto)
    }
    }
    guard let url = url else {
    return
    }
    guard let imageData = try? Data(contentsOf: url) else {
    return
    }
    downloadedPhoto = UIImage(data: imageData)
    self.photoCache[reference] = downloadedPhoto
    }
    .resume()
    }
    }
    }









    share|improve this question

























      0












      0








      0








      I am making a web request to the Google Places API to fetch nearby places based off a specific coordinate and thereafter run a task to fetch a photo of those places.



      I am able to successfully get place details through the first task, and while the second task of retrieving the photo runs, its completion block is never entered resulting in no photo as I have observed placing breakpoints.



      The urlString is correct, my apiKey works correctly since again the first task returns the right response, and I have the photo reference of the place to be able to execute the web request. Any guidance would be appreciated as I am admittedly lost - can provide more detail if needed.



      Below are the network calls, which is adapted from ray wenderlich's guide.



      typealias PlacesCompletion = ([Place]) -> Void
      typealias PhotoCompletion = (UIImage?) -> Void

      class DataProvider {
      private var photoCache: [String: UIImage] = [:]
      private var placesTask: URLSessionDataTask?
      private var session: URLSession {
      return URLSession.shared
      }

      //FIRST WEB REQUEST
      func fetchPlacesNearCoordinate(_ coordinate: CLLocationCoordinate2D, radius: Double, types: [String], completion: @escaping PlacesCompletion) -> Void {
      var urlString = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=(coordinate.latitude),(coordinate.longitude)&radius=(radius)&rankby=prominence&sensor=true&key=(googleApiKey)"
      let typesString = "restaurant"
      urlString += "&type=(typesString)"
      urlString = urlString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) ?? urlString

      guard let url = URL(string: urlString) else {
      completion()
      return
      }

      if let task = placesTask, task.taskIdentifier > 0 && task.state == .running {
      task.cancel()
      }

      DispatchQueue.main.async {
      UIApplication.shared.isNetworkActivityIndicatorVisible = true
      }

      placesTask = session.dataTask(with: url) { data, response, error in
      var placesArray: [Place] =
      defer {
      DispatchQueue.main.async {
      UIApplication.shared.isNetworkActivityIndicatorVisible = false
      completion(placesArray)
      }
      }
      guard let data = data,
      let json = try? JSON(data: data, options: .mutableContainers),
      let results = json["results"].arrayObject as? [[String: Any]] else {
      return
      }
      results.forEach {
      let place = Place(dictionary: $0, acceptedTypes: types)
      placesArray.append(place)

      if let reference = place.photoReference {
      //SECOND WEB REQUEST CALL
      self.fetchPhotoFromReference(reference) { image in
      place.photo = image
      }
      }
      }
      }
      placesTask?.resume()
      }



      //SECOND WEB REQUEST
      func fetchPhotoFromReference(_ reference: String, completion: @escaping PhotoCompletion) -> Void {
      if let photo = photoCache[reference] {
      completion(photo)
      } else {
      let urlString = "https://maps.googleapis.com/maps/api/place/photo?maxwidth=200&maxheight=200&photoreference=(reference)&key=(googleApiKey)"
      guard let url = URL(string: urlString) else {
      completion(nil)
      return
      }

      DispatchQueue.main.async {
      UIApplication.shared.isNetworkActivityIndicatorVisible = true
      }

      session.downloadTask(with: url) { url, response, error in
      var downloadedPhoto: UIImage? = nil
      defer {
      DispatchQueue.main.async {
      UIApplication.shared.isNetworkActivityIndicatorVisible = false
      completion(downloadedPhoto)
      }
      }
      guard let url = url else {
      return
      }
      guard let imageData = try? Data(contentsOf: url) else {
      return
      }
      downloadedPhoto = UIImage(data: imageData)
      self.photoCache[reference] = downloadedPhoto
      }
      .resume()
      }
      }
      }









      share|improve this question














      I am making a web request to the Google Places API to fetch nearby places based off a specific coordinate and thereafter run a task to fetch a photo of those places.



      I am able to successfully get place details through the first task, and while the second task of retrieving the photo runs, its completion block is never entered resulting in no photo as I have observed placing breakpoints.



      The urlString is correct, my apiKey works correctly since again the first task returns the right response, and I have the photo reference of the place to be able to execute the web request. Any guidance would be appreciated as I am admittedly lost - can provide more detail if needed.



      Below are the network calls, which is adapted from ray wenderlich's guide.



      typealias PlacesCompletion = ([Place]) -> Void
      typealias PhotoCompletion = (UIImage?) -> Void

      class DataProvider {
      private var photoCache: [String: UIImage] = [:]
      private var placesTask: URLSessionDataTask?
      private var session: URLSession {
      return URLSession.shared
      }

      //FIRST WEB REQUEST
      func fetchPlacesNearCoordinate(_ coordinate: CLLocationCoordinate2D, radius: Double, types: [String], completion: @escaping PlacesCompletion) -> Void {
      var urlString = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=(coordinate.latitude),(coordinate.longitude)&radius=(radius)&rankby=prominence&sensor=true&key=(googleApiKey)"
      let typesString = "restaurant"
      urlString += "&type=(typesString)"
      urlString = urlString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) ?? urlString

      guard let url = URL(string: urlString) else {
      completion()
      return
      }

      if let task = placesTask, task.taskIdentifier > 0 && task.state == .running {
      task.cancel()
      }

      DispatchQueue.main.async {
      UIApplication.shared.isNetworkActivityIndicatorVisible = true
      }

      placesTask = session.dataTask(with: url) { data, response, error in
      var placesArray: [Place] =
      defer {
      DispatchQueue.main.async {
      UIApplication.shared.isNetworkActivityIndicatorVisible = false
      completion(placesArray)
      }
      }
      guard let data = data,
      let json = try? JSON(data: data, options: .mutableContainers),
      let results = json["results"].arrayObject as? [[String: Any]] else {
      return
      }
      results.forEach {
      let place = Place(dictionary: $0, acceptedTypes: types)
      placesArray.append(place)

      if let reference = place.photoReference {
      //SECOND WEB REQUEST CALL
      self.fetchPhotoFromReference(reference) { image in
      place.photo = image
      }
      }
      }
      }
      placesTask?.resume()
      }



      //SECOND WEB REQUEST
      func fetchPhotoFromReference(_ reference: String, completion: @escaping PhotoCompletion) -> Void {
      if let photo = photoCache[reference] {
      completion(photo)
      } else {
      let urlString = "https://maps.googleapis.com/maps/api/place/photo?maxwidth=200&maxheight=200&photoreference=(reference)&key=(googleApiKey)"
      guard let url = URL(string: urlString) else {
      completion(nil)
      return
      }

      DispatchQueue.main.async {
      UIApplication.shared.isNetworkActivityIndicatorVisible = true
      }

      session.downloadTask(with: url) { url, response, error in
      var downloadedPhoto: UIImage? = nil
      defer {
      DispatchQueue.main.async {
      UIApplication.shared.isNetworkActivityIndicatorVisible = false
      completion(downloadedPhoto)
      }
      }
      guard let url = url else {
      return
      }
      guard let imageData = try? Data(contentsOf: url) else {
      return
      }
      downloadedPhoto = UIImage(data: imageData)
      self.photoCache[reference] = downloadedPhoto
      }
      .resume()
      }
      }
      }






      ios swift google-places-api nsurlsession nsurlsessiondownloadtask






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Nov 20 '18 at 23:40









      ChrisChris

      10911




      10911
























          1 Answer
          1






          active

          oldest

          votes


















          0














          Try removing the session.download(with: url) block. In order to download the data from the url, all you need is let imageData = try? Data(contentsOf: url).



          Also, you are not giving the let imageData = try? Data(contentsOf: url) time to execute. You make the call and then immediately try to use the results in the downloadedPhoto = UIImage(data: imageData) line.






          share|improve this answer























            Your Answer






            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "1"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53403253%2furl-session-download-task-completion-block-never-called%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            0














            Try removing the session.download(with: url) block. In order to download the data from the url, all you need is let imageData = try? Data(contentsOf: url).



            Also, you are not giving the let imageData = try? Data(contentsOf: url) time to execute. You make the call and then immediately try to use the results in the downloadedPhoto = UIImage(data: imageData) line.






            share|improve this answer




























              0














              Try removing the session.download(with: url) block. In order to download the data from the url, all you need is let imageData = try? Data(contentsOf: url).



              Also, you are not giving the let imageData = try? Data(contentsOf: url) time to execute. You make the call and then immediately try to use the results in the downloadedPhoto = UIImage(data: imageData) line.






              share|improve this answer


























                0












                0








                0







                Try removing the session.download(with: url) block. In order to download the data from the url, all you need is let imageData = try? Data(contentsOf: url).



                Also, you are not giving the let imageData = try? Data(contentsOf: url) time to execute. You make the call and then immediately try to use the results in the downloadedPhoto = UIImage(data: imageData) line.






                share|improve this answer













                Try removing the session.download(with: url) block. In order to download the data from the url, all you need is let imageData = try? Data(contentsOf: url).



                Also, you are not giving the let imageData = try? Data(contentsOf: url) time to execute. You make the call and then immediately try to use the results in the downloadedPhoto = UIImage(data: imageData) line.







                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Nov 21 '18 at 16:41









                Key HoffmanKey Hoffman

                94




                94
































                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Stack Overflow!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53403253%2furl-session-download-task-completion-block-never-called%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    Biblatex bibliography style without URLs when DOI exists (in Overleaf with Zotero bibliography)

                    ComboBox Display Member on multiple fields

                    Is it possible to collect Nectar points via Trainline?