Я хочу создать TableView из данных из данных JSON, но когда я пытался получить значение, оно всегда возвращает ноль при второй печати. Не могли бы вы объяснить и дать мне несколько советов по этой проблеме? Я уже очень старался, прежде чем задавать этот вопрос, но я действительно новичок в этом :(

С наилучшими пожеланиями,

import UIKit

struct News: Decodable {
    var status:String
    var totalResults:Int
    var articles:[Articles]?
}

struct Articles: Decodable{
    var source:Source?
    var title:String?
    var description:String?
    var urlToImage:String?
    var publishedAt:String?

}

struct Source: Decodable{
    var name:String?

}

class NewsViewController: UIViewController{
      var a = [String]()

    @IBOutlet weak var TableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        //Hit API
       let url = URL(string: "http://newsapi.org/v2/top-headlines?country=th&category=technology&apiKey=2b27ab9b590041a6a6dcdf4ef94a0a33")

        URLSession.shared.dataTask(with: url!) { (data, response, error) in
            if error == nil {

                do {
                    let result = try JSONDecoder().decode(News.self,from: data!)
                  let totalresult = result.articles!.count


                 for result in result.articles!{
                     let titleUnWrapped: String = result.title ?? ""
                     let urlToImageUnWrapped: String = result.urlToImage ?? ""
                    self.a.append(titleUnWrapped)
                   // print(self.a) <<<<<<<<<<<<<<<<<<<<<<<<< Work
                    }
                } catch {
                    print("ERROR")
                }

        }

        }.resume()

       print(self.a) //Return nil <<<<<<<<<<<<<<<<<<<<<<<<< Not Work
}
}
0
b478 9 Апр 2020 в 06:47
4
Ваш второй оператор печати выполняется сразу после вашего вызова API, но до того, как этот вызов API извлечет данные и завершится, поэтому он все еще пуст. Оператор печати внутри закрытия завершения вызывается только после извлечения данных и показывает правильный результат. Вызов API является асинхронным, поэтому он делает свое дело, но не блокирует поток выполнения вашего кода, поэтому второй оператор печати выполняется, пока a все еще пуст.
 – 
Magnas
9 Апр 2020 в 06:51
Большое спасибо за совет, Magnas!, еще одна проблема заключается в том, как я могу позволить ему дождаться полного извлечения данных, а затем вызвать вторую функцию, чтобы она не возвращала nil? Я уже пробовал с диспетчерской группой, но, похоже, у меня это не работает :(
 – 
b478
9 Апр 2020 в 09:45

1 ответ

Я думаю, что проблема просто в отсутствии опыта работы с замыканиями. Эта часть строки: URLSession.shared.dataTask(with: url!) отправляет запрос на ваш URL-адрес (например, на какой-то удаленный сервер), но не имеет реального представления о том, сколько времени потребуется, чтобы получить ответ. Вместо того, чтобы ждать и блокировать основной поток (неотзывчивый пользовательский интерфейс и плохое взаимодействие с пользователем), он делает свой запрос вне основного потока, и ваш код продолжает выполняться, пока он ожидает. Вот почему последний оператор печати терпит неудачу, но это означает, что пользователь все еще может взаимодействовать с вашим приложением, прокручивая таблицу или что-то еще.

НО, когда ответ в конечном итоге получен от удаленного источника данных, он передается в ваше закрытие, и код внутри выполняется. Итак, на данный момент у вас либо успех (данные хорошие), либо некоторая неудача (проверьте ответ и ошибку). Если у вас есть хорошие данные, вы можете передать их какой-нибудь внешней функции, чтобы продолжить работу с ними. например

class NewsViewController: UIViewController{

   var a = [String]()

    @IBOutlet weak var TableView: UITableView!

    override func viewDidLoad() {
       super.viewDidLoad()

       //Hit API
       let url = URL(string: "http://newsapi.org/v2/top-headlines?country=th&category=technology&apiKey=2b27ab9b590041a6a6dcdf4ef94a0a33")
       URLSession.shared.dataTask(with: url!) { (data, response, error) in
          if error == nil {
             do {
                let result = try JSONDecoder().decode(News.self,from: data!)
                let totalresult = result.articles!.count

                for result in result.articles! {
                   let titleUnWrapped: String = result.title ?? ""
                   let urlToImageUnWrapped: String = result.urlToImage ?? ""
                   self.a.append(titleUnWrapped)
                   // print(self.a) <<<<<<<<<<<<<<<<<<<<<<<<< Work
                   self.doSomethingWithResult(with: a) //<<<< Pass `a` out of the closure
                }
             } catch {
                print("ERROR")
             }
           } else {
              // Error is not nil so do something...
              print("ERROR: \(error)")
        }.resume()
     }

     // This method is called from inside the closure only when you have good
     // data returned from your api call.
     func doSomethingWithResult(with goodData: [String]) {
        // Use `a`...
     }
}

Таким образом, вам не нужно беспокоиться о попытках получить доступ к данным слишком рано или дождаться их завершения, закрытие не будет вызвано до тех пор, пока вы не получите полный ответ, хороший или плохой. Надеюсь это поможет.

2
Magnas 9 Апр 2020 в 12:32