Почему мой обработчик завершения не работает с моим URLSession?

В моем приложении возникла небольшая проблема. Прежде чем вставлять какой-либо код, позвольте мне рассказать немного о контексте. У меня есть TableView с некоторыми динамическими ячейками. Каждая ячейка содержит кнопку. Когда я нажимаю кнопку внутри ячейки, вызывается функция, которая выполняет запрос задачи URLSession. Я использую класс Decodable для хранения результата JSON, и все, что я пытаюсь сделать, это распечатать счетчик массива результатов. Проблема у меня; печать происходит до того, как будет возвращен результат.

Чтобы решить эту проблему, я попытался использовать обработчик завершения и запустить URLSession внутри DispatchGroup и не печатать, пока DispatchGroup не закончится.

Я надеюсь, что кто-нибудь сможет быстро взглянуть на мой код и указать на мою ошибку.

Вот моя функция, в которой я делаю запрос URLSession:

func loadLocalPrice(selectedItemName: String, completion: @escaping (_ result: [PriceModel])-> Void) {

    var localPrice = [PriceModel]()

    let urlApi = "http://my.url.com/category/"+self.selectedCategory+"/"+selectedItemName

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

    URLSession.shared.dataTask(with: url) { (data, response, error) in
        guard let data = data else {return}
        self.dispatchGroupLoadItems.enter()
        do {
            localPrice = try JSONDecoder().decode([PriceModel].self, from: data)
            print("The result inside the function is: \(localPrice.count)")
        } catch let JSONerror {
            print("error decoding JSON", JSONerror)
        }
        self.dispatchGroupLoadItems.leave()

        }.resume()
        return completion(localPrice)
}

Вот функция, которая вызывает указанную выше функцию, содержащую URLSession:

    func addToBasket(sender: UIButton, name: String?, category: String?) {
    var localPrice = [PriceModel]()

    DispatchQueue.main.async {
        self.loadLocalPrice(selectedItemName: name!) {
            (result: [PriceModel]) in
            print("got back inside the dispatchGroup: \(result.count)")

            localPrice = result
        }


        self.dispatchGroupLoadItems.notify(queue: .main) {
            print("Got back outside the dispatchGroup: \(localPrice.count)")
        }
    }
}

Ниже приведен вывод моей консоли, чтобы вы могли видеть порядок, в котором возвращаются результаты:

got back inside the dispatchGroup: 0
Got back outside the dispatchGroup: 0
The result inside the function is: 1

person JamMan9    schedule 17.07.2018    source источник
comment
вы помещаете return в неправильное место, сохраните его перед печатью (результат внутри функции: (localPrice.count)) этот оператор   -  person chirag shah    schedule 17.07.2018


Ответы (3)


Вам просто не нужен dispatchGroup, если у вас уже есть обработчик завершения. Вашу проблему можно решить, правильно используя обработчик completion, как показано ниже.

func loadLocalPrice(selectedItemName: String, completion: @escaping (_ result: [PriceModel])-> Void) {

    var localPrice = [PriceModel]()

    let urlApi = "http://my.url.com/category/"+self.selectedCategory+"/"+selectedItemName

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

    URLSession.shared.dataTask(with: url) { (data, response, error) in
        guard let data = data else {
               completion(localPrice)
               return
             }
        do {
            localPrice = try JSONDecoder().decode([PriceModel].self, from: data)
        } catch let JSONerror {
            print("error decoding JSON", JSONerror)
        }
        DispatchQueue.main.async {
          completion(localPrice)
        }
     }.resume() 
}

func addToBasket(sender: UIButton, name: String?, category: String?) {
    var localPrice = [PriceModel]()

    self.loadLocalPrice(selectedItemName: name!) {
        (result: [PriceModel]) in
        print("Count: \(result.count)")
        localPrice = result
    }
}
person Kamran    schedule 17.07.2018
comment
Вы должны вызвать завершение (localPrice) после блока do {}, как будто в случае ошибки он не будет вызываться. - person whoover; 17.07.2018
comment
Спасибо за ответ и объяснение. Также спасибо за вставку моего кода обратно в исправленный. Единственное изменение, которое я сделал, - это переместить завершение после проверки ошибок, как было предложено выше. - person JamMan9; 17.07.2018

Вам не нужна группа отправки и возвращайтесь сюда.

 func loadLocalPrice(selectedItemName: String, completion: @escaping (_ result: [PriceModel])-> Void) {

      var localPrice = [PriceModel]()

      let urlApi = "http://my.url.com/category/"+self.selectedCategory+"/"+selectedItemName

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

    URLSession.shared.dataTask(with: url) { (data, response, error) in
        guard let data = data else {
            completion(localPrice)
            return
        }
        do {
            localPrice = try JSONDecoder().decode([PriceModel].self, from: data)
            print("The result inside the function is: \(localPrice.count)"  
        } catch let JSONerror {
            print("error decoding JSON", JSONerror)
        }
        completion(localPrice)
    }.resume()
}
person whoover    schedule 17.07.2018

Проблема в том, что вы пытаетесь return completion(localPrice). Во-первых, URLSession.shared.dataTask(with: url) работает в фоновом режиме, поэтому вам не нужно использовать dispatchGroupLoadItems.

Все, что вам нужно сделать, это вызвать вас completion block в основном потоке в URLSession.shared.dataTask(with: url) completion block, вот так:

func loadLocalPrice(selectedItemName: String, completion: @escaping (_ result: [PriceModel])-> Void) {
    var localPrice = [PriceModel]()
    let urlApi = "http://my.url.com/category/"+self.selectedCategory+"/"+selectedItemName
    guard let url = URL(string: urlApi) else {
        completion(localPrice)
        return
    }

    URLSession.shared.dataTask(with: url) { (data, response, error) in
        guard let data = data else {return}
        do {
            localPrice = try JSONDecoder().decode([PriceModel].self, from: data)
            print("The result inside the function is: \(localPrice.count)")
        } catch let JSONerror {
            print("error decoding JSON", JSONerror)
        }

        DispatchQueue.main.async {
            completion(localPrice)
        }
    }.resume()
}

И функция addToBasket стала:

func addToBasket(sender: UIButton, name: String?, category: String?) {
    var localPrice = [PriceModel]()

    self.loadLocalPrice(selectedItemName: name!) {(result: [PriceModel]) in
        print("The result from completion block: \(result.count)")

        localPrice = result
    }
}
person Ido    schedule 17.07.2018