Почему JSON данные из декодера, чтобы стать источником данных UITableView не присваивает? - PullRequest
0 голосов
/ 05 февраля 2020

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

(1) Функция извлекает данные из API Open Weather Map (определено в отдельном классе 'GetWeather').

func getMowForecast(completion: @escaping ((WeatherForecast?, Bool)) -> Void) {
    let url = URL(string: "http://api.openweathermap.org/data/2.5/forecast?id=524901&APPID=b3d57a41f87619daf456bfefa990fce4&units=metric")!
    let request = URLRequest(url: url)
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        if let data = data {
            do {
                let json = try JSONDecoder().decode(WeatherForecast.self, from: data)
                completion((json, true))
            } catch {
                print(error)
                completion((nil, false))
            }
        } else {
            print(error)
        }
    }
    task.resume()
}

Здесь все отлично работает. JSON загружается правильно и соответствует модели данных. Вот ссылка на JSON данные для отображения в tableView: https://pastebin.com/KkXwxYgS

(2) Контроллер обрабатывает отображение полученных JSON данных в формате tableView

import UIKit
class ForecastViewController: UITableViewController {

    @IBOutlet weak var tableV: UITableView! // tableView outlet in the IB

    let weatherGetter = GetWeather() // object to handle the JSON retrieval

    var tableData: WeatherForecast? // tableView data source

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.tableData?.list.count ?? 0
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! TableVCCell
        cell.dateLabel.text = "\(self.tableData?.list[indexPath.row].dt)"
        cell.tempLabel.text = "\(self.tableData?.list[indexPath.row].main.temp)"
        cell.feelsLikeLabel.text = "\(self.tableData?.list[indexPath.row].main.feels_like)"
        return cell
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        navigationController?.navigationBar.prefersLargeTitles = true
        tableV.delegate = self
        tableV.dataSource = self

        weatherGetter.getMowForecast { (data, status) in
            if let data = data, status {
            } else if status {
                print("-------- Ошибка разбора данных прогноза погоды --------")
            } else {
                print("-------- Ошибка получения данных прогноза погоды --------")
            }
            self.tableData = data
            print(self.tableData)
        }

        print(self.tableData?.list.count) // returns nil

        self.tableData = weatherGetter.getMowForecast(completion: ((tableData, true))) // error - Cannot convert value of type '(WeatherForecast?, Bool)' to expected argument type '((WeatherForecast?, Bool)) -> Void'
    }
    }

Проблема в том, что табличное представление получает нулевой источник данных, поэтому оно не может загрузить данные и показывает пустой экран.

Я полагаю, ошибка находится в области действия - я пытаюсь получить данные JSON внутри функции, а они не go где-либо еще. Что меня интересует, так это то, почему назначение данных для self.tableData не оказывает никакого влияния?

Не могли бы вы помочь. Спасибо! Привет

Ответы [ 2 ]

1 голос
/ 05 февраля 2020

Прежде всего удалить

print(self.tableData?.list.count) // returns nil

self.tableData = weatherGetter.getMowForecast(completion: ((tableData, true))) // error - Cannot convert value of type '(WeatherForecast?, Bool)' to expected argument type '((WeatherForecast?, Bool)) -> Void'

Ошибка возникает из-за того, что метод ничего не возвращает и синтаксис обработчика завершения имеет вид неправильно. Обе строки в любом случае бессмысленны из-за асинхронного поведения getMowForecast

Во-вторых, я рекомендую объявить массив источника данных как необязательный массив типа, который представляет List. Затем вы избавляетесь от всех этих ненужных опций.

var tableData = [List]()

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return self.tableData.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! TableVCCell
    let weatherData = self.tableData[indexPath.row]
    cell.dateLabel.text = "\(weatherData.dt)"
    cell.tempLabel.text = "\(weatherData.main.temp)"
    cell.feelsLikeLabel.text = "\(weatherData.main.feels_like)"
    return cell
}

Чтобы иметь возможность отображать данные - как уже упоминалось другими - вы должны перезагрузить табличное представление в обработчике завершения. И назначайте данные только в том случае, если status равно true.

override func viewDidLoad() {
    super.viewDidLoad()
    navigationController?.navigationBar.prefersLargeTitles = true
    tableV.delegate = self
    tableV.dataSource = self

    weatherGetter.getMowForecast { [weak self] (data, status) in
        if let data = data, status {
           self?.tableData = data.list
           DispatchQueue.main.async {
              self?.tableV.reloadData()
           }
        } else if status {
            print("-------- Ошибка разбора данных прогноза погоды --------")
        } else {
            print("-------- Ошибка получения данных прогноза погоды --------")
        }            
    }
}

. И учтите, что сообщение Ошибка разбора данных прогноза погоды никогда не будет отображаться.

0 голосов
/ 05 февраля 2020

Вам необходимо перезагрузить таблицу внутри обратного вызова, так как она асинхронная

 self.tableData = data
 print(self.tableData) 
 DispatchQueue.main.async { self.tableV.reloadData() } 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...