Ошибка индексации вне диапазона при попытке доступа к данным JSON из структур - PullRequest
0 голосов
/ 13 ноября 2018

Я пытаюсь получить данные о запасах из финансового API IEX, и все, что мне нужно, это просто вывод на консоль, но у меня возникают проблемы при попытке использовать эти данные для подключения к моему табличному представлению.

Это URL-адрес API , а также JSON в удобочитаемом формате .

Это структуры, которые я создал

struct Welcome: Decodable {
    let aapl, fb, msft, tsla, goog: Aapl

    enum CodingKeys: String, CodingKey {
        case aapl = "AAPL"
        case fb = "FB"
        case msft = "MSFT"
        case tsla = "TSLA"
        case goog = "GOOG"
    }
}

struct Aapl: Decodable {
    let quote: Quote
    let news: [News]
}

struct Quote: Decodable {
    let symbol: String
    let companyName: String
    let latestPrice: Double
}

struct News: Decodable {
    let url: String
    let image: String
}

Вот как данные выбираются:

fetchData(url: stockApiUrl) { (result: FetchResult<Welcome>) -> (Void) in
    switch result {
    case .success(let object): self.stockData = [object]
        print("stockData: \n\n\(self.stockData)")
    case .failure(let error):
        print("Error decoding JSON: \n\n\(error)")
    }
    DispatchQueue.main.async {
        self.tableView.reloadData()
    }
}

И передать данные в

var stockData = [Welcome]()

Имея код, который есть сейчас, он выводит все данные, которые мне нужны для компаний, но я не совсем понимаю, как получить доступ ко всем данным компании, чтобы я мог получить каждый кусок (companyName, latestPrice и т. д.) по отдельности, чтобы я мог отобразить их в виде таблицы.

Например, в моем методе cellForRowAt табличного представления я пытаюсь установить companyName для меток, но использование let quotes = stockData[indexPath.row] дает мне индекс вне диапазона ошибок, поэтому я не могу тогда использовать что-то вроде cell.companyNameLabel = quotes[indexPath.row].companyName.

Мне интересно, нужно ли мне реструктурировать свои структуры (хотя я сравнил свой код с предложенным кодом из quicktype.io и он идентичен) или переделать что-то еще.

Спасибо за любые советы!

EDIT:

numberOfRows метод:

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if section == 0 {
        return stockData.count
    } else if section == 1 && fetchingMore {
        return 1
    }
    return 0
}

fetchData функция:

func fetchData<T: Decodable>(url: URL, completion: @escaping (FetchResult<T>) -> (Void)) {

    URLSession.shared.dataTask(with: url) { (data, response, error) in

        guard let data = data else {completion(.failure(error!)); return}

        do {
            //let object = try JSONDecoder().decode(T.self, from: data)
            let object = try? JSONDecoder().decode([String:Aapl].self,from: data)
            completion(.success(object))
        } catch {
            completion(.failure(error))
        }
    }.resume()
}

Ответы [ 3 ]

0 голосов
/ 13 ноября 2018

Вы можете сделать

 var welcomes:[String:Aapl]! 

 welcomes = try? JSONDecoder().decode([String:Aapl].self,from:jsonData)

затем используйте это внутри numberOfRowsInSection

return welcomes.keys.count

и внутри cellForRowAt

let item = welcomes[welcomes.keys[indexPath.row]]!.quote.companyName

Используйте его, если вам не нужна сортировка

0 голосов
/ 13 ноября 2018

Чтобы использовать более общее и описательное имя, переименуйте Aapl в Stock

struct Stock: Decodable {
    let quote: Quote
    let news: [News]
}

Объявите stockData в качестве словаря и создайте массив для ключей

var stockData = [String:Stock]()
var keys = [String]()

Заменить fetchData на

fetchData(url: stockApiUrl) { (result: FetchResult<[String:Stock]>) -> (Void) in
    switch result {
    case .success(let object): 
        self.stockData = object
        self.keys = Array(object.keys)
        print("stockData: \n\n\(self.stockData)")
    case .failure(let error):
        print("Error decoding JSON: \n\n\(error)")
    }
    DispatchQueue.main.async {
        self.tableView.reloadData()
    }
}

В cellForRow написать

let stock = stocks[keys[indexPath.row]]!
cell.companyNameLabel = stock.quote.companyName
0 голосов
/ 13 ноября 2018

Эта проблема в основном вызвана тем, что вы DataSource - это что-то еще.

, например, если вы задали numberForRows in section с помощью welcome.count и использовали что-то еще для итерации внутри cellForRowAt, если эта вещьнесовпадение числа numberForRows при условии, что это приведет к тому, что

индекс выйдет за пределы диапазона Ошибка

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

Теперь в вашем случае, я не знаю, как вы установили numberOfRows, но предполагаю, что это что-то другое.

Я бы предложил следующее.

вместо итерации quotes[indexPath.row] вместо этого используйте тот же DataSource и продолжайте свой путь, я предполагаю, что это stockData, так что предположим, что это похоже на то, как ваш structs выглядит как

stockData[indexPath.row].qoutes.companyName

ОБНОВЛЕНИЕ

, так что для вашей проблемы JSON само по себе тоже не является array, поэтому нужно немного поработать, чтобы создать окончательный массив каждого Aapl вариант.

Вы можете создать его вручную в didSet из stockData, который возвращает вам массив и использовать его вместо этого для отображения данных и повторения.

Этот код должен дать вам представление.

 var myFinalData: [Aapl] = [] {
    didSet {
        //reload your table
    }
}
var stockData = [Welcome]() {
    didSet{
        myFinalData.append(stockData.aapl)
        myFinalData.append(stockData.fb)
        // etc
    }
}
// then you can use myFinalData as you DataSource and iterate easily inside of it 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...