尝试从结构访问JSON数据时出现索引超出范围错误

时间:2018-11-13 18:40:43

标签: ios json swift struct

我正在尝试从IEX finance API中获取库存数据,并且我需要的一切都可以很好地输出到控制台,但是在尝试使用该数据插入表格视图时遇到了问题。

这是API url,这是JSON in a readable format

这些是我设置的结构

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,{ {1}}等),这样我就可以在表格视图中显示它们。

例如,在我的latestPrice表格视图方法中,我试图将cellForRowAt设置为标签,但是使用companyName会给我索引超出范围的错误,因此我可以然后使用类似let quotes = stockData[indexPath.row]之类的东西。

我想知道是否需要重组我的结构(尽管我将我的代码与quicktype.io的建议代码进行了比较,并且是相同的),还是重新进行了其他工作。

感谢您的指导!

编辑:

cell.companyNameLabel = quotes[indexPath.row].companyName方法:

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

3 个答案:

答案 0 :(得分:1)

你可以做

 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

如果您不关心排序,请使用

答案 1 :(得分:1)

要使用更通用和更具描述性的名称,请将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

答案 2 :(得分:0)

此问题主要是由于您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的{​​{1}}中手动创建一个数组,该数组将返回一个数组,并使用该数组显示数据并进行迭代。

此代码应给您一个想法。

 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