首页 > 解决方案 > Swift + macOS + Xcode:从 ContentView 访问在类中创建的键值对数组

问题描述

我编写了一些代码,成功地将 JSON 文件读入我认为是一个键值对数组,每个键有 5 个值。JSON 文件在创建类的过程中被读入,并且似乎可以工作。在 ContentView 中,我希望能够单步执行键(即日期)和值,可能使用 ForEach 循环。不幸的是,我无法从 ContentView 访问键值对数组。类中的打印语句打印出我所期望的。在 ContentView 中,arrayname.count 返回 0。我想我忽略了一些简单的事情,但有时当你看一些东西太久时,你会错过显而易见的事情。如果有人能指出我正确的方向,将不胜感激。

ForEach 是我尝试确定如何访问 vooData 的各个成员的方式,例如日期,它是键,或特定值,例如关闭。

下面是我的测试代码。问候,克里斯

我的 JSON 文件示例和 ReadData 类中 print 语句的结果如下所示。

{
    "Meta Data": {
        "1. Information": "Daily Prices (open, high, low, close) and Volumes",
        "2. Symbol": "VOO",
        "3. Last Refreshed": "2021-09-22",
        "4. Output Size": "Full size",
        "5. Time Zone": "US/Eastern"
    },
    "Time Series (Daily)": {
        "2021-09-22": {
            "1. open": "402.1700",
            "2. high": "405.8500",
            "3. low": "401.2600",
            "4. close": "403.9000",
            "5. volume": "5979811"
        },

Stock(timeSeriesDaily: [

"2011-07-22": ReadJSONData.TimeSeriesDaily
(Open: "61.5000", 
High: "61.6000", 
Low: "61.2000", 
Close: "61.5500", 
Volume: “180400"
), 

"2014-06-18": ReadJSONData.TimeSeriesDaily
(
Open: "178.5900", 
High: "179.9700", 
Low: "178.1700", 
Close: "179.8700", 
Volume: “501900"
), 

"2015-09-18": ReadJSONData.TimeSeriesDaily
(
Open: "180.4100", 
High: "182.0400", 
Low: "179.6900", 
Close: "180.1300", 
Volume: “3205186"
)
])



import SwiftUI
struct Stock: Codable {
    let timeSeriesDaily: [String: TimeSeriesDaily]
    enum CodingKeys: String, CodingKey {
        case timeSeriesDaily = "Time Series (Daily)"
    }
}
struct TimeSeriesDaily: Codable {
    let Open, High, Low, Close: String
    let Volume: String
    enum CodingKeys: String, CodingKey {
        case Open = "1. open"
        case High = "2. high"
        case Low = "3. low"
        case Close = "4. close"
        case Volume = "5. volume"
    }
}
class ReadData {
//class ReadData: ObservableObject  {
//    @Published var tmpData  = [Stock]()
    var tmpData : [Stock] = []
    init() {
        loadData()
    }
    func loadData() {
        guard let url = Bundle.main.url(forResource: "VOO", withExtension: "json")
        else {
            print("Json file not found")
            return
        }
        let data = try? Data(contentsOf: url)
        let tmpData = try? JSONDecoder().decode(Stock.self, from: data!)
        print(tmpData!)
    }
}
struct ContentView: View {
//    @ObservedObject var vooData = ReadData()
    let readClass = ReadData()
    @State var vooData : [Stock] = ReadData().tmpData
    var body: some View {
        VStack{
            Text("Hello, world!")
                .padding()
                .frame(width: 600, height: 400, alignment: .center)
                .onAppear(perform: {
                    print(vooData.count)
                })
        }
    }
}

标签: arraysswiftclasskey-value

解决方案


您包含的 JSON 不是真正有效的。我假设它实际上看起来像这样:

{
    "Time Series (Daily)": {
        "2021-09-22": {
            "1. open": "402.1700",
            "2. high": "405.8500",
            "3. low": "401.2600",
            "4. close": "403.9000",
            "5. volume": "5979811"
        }
    }
}

如果是这种情况,那么您可以对您的代码进行一些细微的改动并让它工作。

  • 看起来您最初尝试使用ObservableObject- 这就是您应该使用的。

  • 在您的原始代码中,您使用let tmpData =但从未设置self.tmpData

  • 使用do/try/catch而不是try?更可取,因为如果发生错误,您实际上可以打印错误。

  • 目前尚不清楚您要显示的确切内容,但我提供了一个ForEach将字典转换为键/值对然后显示开盘价的示例。

struct TimeSeriesDaily: Codable {
    let Open, High, Low, Close: String
    let Volume: String
    enum CodingKeys: String, CodingKey {
        case Open = "1. open"
        case High = "2. high"
        case Low = "3. low"
        case Close = "4. close"
        case Volume = "5. volume"
    }
}
class ReadData: ObservableObject  {
    @Published var tmpData  = Stock(timeSeriesDaily: [:])
    
    init() {
        loadData()
    }
    
    func loadData() {
        guard let url = Bundle.main.url(forResource: "VOO", withExtension: "json")
        else {
            print("Json file not found")
            return
        }
        do {
            let data = try Data(contentsOf: url)
            self.tmpData = try JSONDecoder().decode(Stock.self, from: data)
            print(self.tmpData)
        } catch {
            print(error)
        }
    }
}
struct ContentView: View {
    @ObservedObject var vooData = ReadData()
    
    var body: some View {
        VStack{
            ForEach(vooData.tmpData.timeSeriesDaily.map { ($0.key,$0.value) }, id: \.0) { keyValuePair in
                VStack {
                    Text(keyValuePair.0)
                    Text("Open: \(keyValuePair.1.Open)")
                }
            }
        }
    }
}

推荐阅读