I have been working with reading JSON files. With help I have been able to do so but now want to expand what I read from the JSON file. I think I have the structures set up correctly but do not understand how to initialize the metaData variable in the Stock structure when I create an instance of the structure from the ReadData class. I have placed ?'s in the line of code in the ReadData that is giving me problems Any help would be appreciated. Chris Below is the JSON data and my code.
"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"
},
struct Stock: Codable {
let metaData: MetaData
let timeSeriesDaily: [String : TimeSeriesDaily] // creates a dictionary, key = string : TimeSeriesDaily = value
enum CodingKeys: String, CodingKey {
case metaData = "Meta Data"
case timeSeriesDaily = "Time Series (Daily)"
}
}
struct MetaData: Codable {
let Information : String
let Symbol : String
let LastRefreshed : String
let OutputSize : String
let TimeZone: String
enum CodingKeys: String, CodingKey {
case Information = "1. Information"
case Symbol = "2. Symbol"
case LastRefreshed = "3. Last Refreshed"
case OutputSize = "4. Output Size"
case TimeZone = "5. Time Zone"
}
}
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(metaData: ????? , timeSeriesDaily : [:])
init() {
loadData()
}
func loadData() {
guard let url = Bundle.main.url(forResource: "VOO", withExtension: "json")
else {
print("Json file not found")
return
}
let decoder = JSONDecoder()
do {
let data = try Data(contentsOf: url)
self.tmpData = try decoder.decode(Stock.self, from: data)
// print(self.tmpData)
} catch {
print(error)
}
}
}
struct ContentView: View {
@ObservedObject var vooData = ReadData()
var body: some View {
ScrollView {
VStack (alignment: .leading) {
let lastYear = getOneYearAgo()
let filteredDict = vooData.tmpData.timeSeriesDaily.filter { $0.key > lastYear } // Works
// let sortedFilteredDict = filteredDict.sorted { $0.key < $1.key } // Works
// let justCloseArray = sortedFilteredDict.map { ($0.key, $0.value.Close) } // returns array [(String, String)]
// let justCloseDict = Dictionary(uniqueKeysWithValues: justCloseArray) // returns dictionary with 1 key & 1 val
// let sortedCloseDict = justCloseDict.sorted { $0.key < $1.key } // works
let newDict = filteredDict.mapValues { Double($0.Close) }
let sortedNewDict = newDict.sorted { $0.key < $1.key }
Spacer()
// ForEach ( sortedCloseDict.map { ($0.key, $0.value) }, id: \.0 ) { keyValuePair in
ForEach ( sortedNewDict.map { ($0.key, $0.value) }, id: \.0 ) { keyValuePair in // map converts dictionary to arrap
HStack {
Text (keyValuePair.0)
Text ("\(keyValuePair.1!)")
}
}
Spacer()
} // end vstack
} .frame(width: 600, height: 400, alignment: .center) // end scroll view
}
}
CodePudding user response:
You need to pass a MetaData
struct to it. The initializer for MetaData
takes a number of Strings
that match its properties:
@Published var tmpData = Stock(metaData: MetaData(Information: "", Symbol: "", LastRefreshed: "", OutputSize: "", TimeZone: ""), timeSeriesDaily: [:])
One hint is that Xcode will autocomplete a lot of this for you. If you start typing Stock
and let autocomplete do the work, you'll see it generate templates for the metaData
and timeSeriesDaily
parameters.