Swift + macOS + Xcode :从 ContentView 访问在 class 中创建的键值对数组

Swift + macOS + Xcode : Access array of key value pairs created in a class from ContentView

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

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

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

我的JSON文件和classReadData中打印语句的结果如下

{
    "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)
                })
        }
    }
}

您所包含的 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 示例,它将字典转换为 key/value 对,然后显示开盘价.

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 { ([=11=].key,[=11=].value) }, id: \.0) { keyValuePair in
                VStack {
                    Text(keyValuePair.0)
                    Text("Open: \(keyValuePair.1.Open)")
                }
            }
        }
    }
}

我认为你的 init

有一点小错误

你有

class ReadData {
    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!)

     // ^^^^^^^^^^^ - don't think this should be let

        print(tmpData!)
    }
}

但我认为您不需要 loadData 内的 tmpData 上的 let。我认为您打算将其分配给实例变量,而不是局部变量。

尝试:

//
//  ContentView.swift
//  JSONSample
//
//  Created by Scott Thompson on 9/23/21.
//

import SwiftUI

let jsonData = """
{
    \"Meta Data\": {
        \"1. Information\": \"Daily Prices (open, high, low, close) and Volumes\",
        \"2. Symbol\": \"VOO\",
        \"3. Last Refreshe\": \"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\"
        },
    }
}
"""

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 {
    let tmpData : [Stock]
    init() {
        let data = jsonData.data(using: .utf8)

        if let parsedData = try? JSONDecoder().decode(Stock.self, from: data!) {
            tmpData = [parsedData]
        } else {
            tmpData = []
        }
    }
}

struct ContentView: View {
    @State var vooData : [Stock] = ReadData().tmpData
    var body: some View {
        VStack{
            Text("Hello, world!")
                .padding()
                .frame(width: 600, height: 400, alignment: .center)
                .onAppear(perform: {
                    debugPrint(vooData)
                    print(vooData.count)
                })
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
            .previewLayout(PreviewLayout.sizeThatFits)
            .padding()
            .previewDisplayName("Default preview")
    }
}