JSON Swift unwrap [""] - json

Im having an issue unwrapping my string correctly with SwiftyJSON
output: ["AAPL"]
stockData = try Data(contentsOf: url!)
let json = try JSON(data: stockData)
if let jsonArray = json["Stock Quotes"].array {
let ticker = String(describing: jsonArray.map({$0["1. symbol"].stringValue}))
print(ticker)
}
JSON
sorry here is the full JSON
{
"Meta Data": {
"1. Information": "Batch Stock Market Quotes",
"2. Notes": "IEX Real-Time Price provided for free by IEX (https://iextrading.com/developer/).",
"3. Time Zone": "US/Eastern"
},
"Stock Quotes": [
{
"1. symbol": "AAPL",
"2. price": "168.3500",
"3. volume": "34941964",
"4. timestamp": "2018-04-06 16:55:36"
}
]
}

if let jsonArray = json["Stock Quotes"].array {
if let ticker = jsonArray[0]["1. symbol"] as? String {
print(ticker)
}
}

Can you try
if let jsonArray = json["Stock Quotes"].array {
if let ticker = jsonArray[0]["1. symbol"] as? String {
print(ticker)
}
}

Related

Trying to get the first element of JSON array Swift

I'm trying to implement a stock API. I have a JSON example:
{
"Meta Data": {
"1. Information": "Daily Prices (open, high, low, close) and Volumes",
"2. Symbol": "DAI.DEX",
"3. Last Refreshed": "2022-04-05",
"4. Output Size": "Full size",
"5. Time Zone": "US/Eastern"
},
"Time Series (Daily)": {
"2022-04-05": {
"1. open": "64.4900",
"2. high": "64.8200",
"3. low": "62.6200",
"4. close": "62.9600",
"5. volume": "3425810"
},
"2022-04-04": {
"1. open": "63.9900",
"2. high": "64.5400",
"3. low": "62.8100",
"4. close": "64.2600",
"5. volume": "2538008"
}
}
I'm trying to display the latest price so I always need the first element in Time Series Daily. In this example 2022-04-05. The list goes on for 20 years. I tried this:
var latestClose: String {
timeSeriesDaily.first?.value.close ?? ""
}
But every time I rerun the app it displays different values and not constantly the first value.
Here my Code:
struct StockData: Codable {
var metaData: MetaData
var timeSeriesDaily: [String: TimeSeriesDaily]
var latestClose: String {
timeSeriesDaily.first?.value.close ?? ""
}
private 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
private 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 {
var open: String
var high: String
var low: String
var close: String
var volume: String
private enum CodingKeys: String, CodingKey {
case open = "1. open"
case high = "2. high"
case low = "3. low"
case close = "4. close"
case volume = "5. volume"
}
}
}
There is no first element because the object is a dictionary which is unordered by definition.
To get the most recent item you have to get the dictionary keys (this returns an array) and sort them descending. The most recent date is the first item.
var latestClose: String {
guard let mostRecentDate = timeSeriesDaily.keys.sorted(by: >).first else { return "" }
return timeSeriesDaily[mostRecentDate]!.close
}

Why JSONDecoder doesn't work with a date format?

I have a simple structs to represent a JSON about a stock:
struct Share: Decodable {
enum CodingKeys: String, CodingKey {
case metaData = "Meta Data"
case prices = "Time Series (Daily)"
}
var metaData: [String: String]
var prices: [Date: TradingDay]
}
struct TradingDay: Decodable {
enum CodingKeys: String, CodingKey {
case open = "1. open"
case close = "4. close"
case high = "2. high"
case low = "3. low"
}
enum DecodingErrors: Error {
case canNotMakeDouble
case invalidCodingKeys
}
var open: Double
var close: Double
var high: Double
var low: Double
init(from decoder: Decoder) throws {
guard let container = try? decoder.container(keyedBy: CodingKeys.self) else {
throw DecodingErrors.invalidCodingKeys
}
guard let open = try? Double(container.decode(String.self, forKey: .open)),
let close = try? Double(container.decode(String.self, forKey: .close)),
let high = try? Double(container.decode(String.self, forKey: .high)),
let low = try? Double(container.decode(String.self, forKey: .low)) else {
throw DecodingErrors.canNotMakeDouble
}
self.open = open
self.close = close
self.high = high
self.low = low
}
}
JSON looks like this:
{
"Meta Data": {
"1. Information": "Daily Prices (open, high, low, close) and Volumes",
"2. Symbol": "IBM",
"3. Last Refreshed": "2022-07-01",
"4. Output Size": "Compact",
"5. Time Zone": "US/Eastern"
},
"Time Series (Daily)": {
"2022-07-01": {
"1. open": "141.0000",
"2. high": "141.6700",
"3. low": "139.2600",
"4. close": "141.1200",
"5. volume": "4012106"
},
"2022-06-30": {
"1. open": "139.5800",
"2. high": "142.4600",
"3. low": "139.2800",
"4. close": "141.1900",
"5. volume": "4878020"
},
and so on.
I'm trying to decode this with the following code:
if let (data, _) = try? await URLSession.shared.data(for: request) {
let decoder = JSONDecoder()
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
decoder.dateDecodingStrategy = .formatted(formatter)
do {
let share = try decoder.decode(Share.self, from: data)
print(share.metaData)
print(share.prices)
} catch {
print(error.localizedDescription)
}
}
But all I see is just an error.
When I make a prices dictionary (7th row) [String: TradingDay] type, it works appropriately.
Why doesn't it decode the dates, though I passed in the date format?
Thanks everyone.

How to parse JSON in Swift with values as keys and dictionaries as arrays [duplicate]

This question already has answers here:
How can I decode a dictionary from a JSON when I don't know the keys of the dictionary? [duplicate]
(2 answers)
Closed 2 years ago.
I tried to parse JSON data from a website api in Swift, but the api is a bit weird. What works is the following: [https://www.avanderlee.com/swift/json-parsing-decoding/][1]
This is a structure to parse/translate:
{
"Meta Data": {
"1. Information": "some info",
"2. Symbol": "string symbol",
"3. Last Refreshed": "2020-03-20 16:00:00",
"4. Interval": "5min",
"5. Output Size": "Compact",
"6. Time Zone": "US/Eastern"
},
"Time Series (5min)": {
"2020-03-20 16:00:00": {
"1. keyABC": "138.2700",
"2. keyCBA": "138.4900",
"3. keyCAB": "136.6500",
"4. keyACB": "136.7800",
"5. keyBAC": "3392530"
},
"2020-03-20 15:55:00": {
"1. keyABC": "137.7825",
"2. keyCBA": "139.9112",
"3. keyCAB": "137.0365",
"4. keyACB": "138.2925",
"5. keyBAC": "2463243"
},
"2020-03-20 15:50:00": {
"1. keyABC": "139.0000",
"2. keyCBA": "139.0150",
"3. keyCAB": "137.7500",
"4. keyACB": "137.7500",
"5. keyBAC": "1051283"
},
...
}
}
Problem 1: There are random-keys as values that I need and don't know how to parse without just leaving deleting
Problem 2: The JSON Libraries are not in an array instead in a new object. But the goal is to make a Swift array out of it
I wonder if there is an easy way to parse something like above with JSONDecode (and if there is one, what's the best?).
app.quicktype.io provides a pretty good starting point for parsing this JSON:
struct JSONData: Codable {
let metaData: MetaData
let timeSeries5Min: [String: TimeSeries5Min]
enum CodingKeys: String, CodingKey {
case metaData = "Meta Data"
case timeSeries5Min = "Time Series (5min)"
}
}
struct MetaData: Codable {
let the1Information, the2Symbol, the3LastRefreshed, the4Interval: String
let the5OutputSize, the6TimeZone: String
enum CodingKeys: String, CodingKey {
case the1Information = "1. Information"
case the2Symbol = "2. Symbol"
case the3LastRefreshed = "3. Last Refreshed"
case the4Interval = "4. Interval"
case the5OutputSize = "5. Output Size"
case the6TimeZone = "6. Time Zone"
}
}
struct TimeSeries5Min: Codable {
let the1KeyABC, the2KeyCBA, the3KeyCAB, the4KeyACB: String
let the5KeyBAC: String
enum CodingKeys: String, CodingKey {
case the1KeyABC = "1. keyABC"
case the2KeyCBA = "2. keyCBA"
case the3KeyCAB = "3. keyCAB"
case the4KeyACB = "4. keyACB"
case the5KeyBAC = "5. keyBAC"
}
}
Given that it should be fairly easy to extract whatever data you need into an array.

How to decode JSON in swift

{
"Meta Data": {
"1. Information": "Daily Prices (open, high, low, close) and Volumes",
"2. Symbol": "FB",
"3. Last Refreshed": "2017-08-23 16:00:00",
"4. Output Size": "Compact",
"5. Time Zone": "US/Eastern"
},
"Time Series (Daily)": {
"2017-08-23 16:00:00": {
"1. open": "168.8400",
"2. high": "169.3600",
"3. low": "168.2000",
"4. close": "168.7100",
"5. volume": "8198515"
},
"2017-08-22": {
"1. open": "168.2800",
"2. high": "169.8700",
"3. low": "167.1500",
"4. close": "169.6400",
"5. volume": "11333260"
},
"2017-08-21": {
"1. open": "167.1600",
"2. high": "168.0000",
"3. low": "165.8200",
"4. close": "167.7800",
"5. volume": "11880823"
},
"2017-08-18": {
"1. open": "166.8400",
"2. high": "168.6700",
"3. low": "166.2100",
"4. close": "167.4100",
"5. volume": "14933261"
},
"2017-08-17": {
"1. open": "169.3400",
"2. high": "169.8600",
"3. low": "166.8500",
"4. close": "166.9100",
"5. volume": "16791591"
},
"2017-08-16": {
"1. open": "171.2500",
"2. high": "171.3800",
"3. low": "169.2400",
"4. close": "170.0000",
"5. volume": "15580549"
}
}
}
My problem is how can I get all the information (ex: 4.close) from the JSON date if the days (ex: 2017-08-02) are constantly changing.
So far in my project I can only get a data of a certain date.
func fetchStockDataCalendar() {
let url = URL(string: "https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=\(symbol)&apikey=\(apiKey)")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print ("ERROR")
} else {
if let content = data {
do {
//Array
let myJson = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
if let Time = myJson["Time Series (Daily)"] as? NSDictionary {
if let Day = Time["2017-06-21"] as? NSDictionary {
if let CloseStockData = Day["4. close"] as? String {
print("2017-06-21 CloseStock-> \(CloseStockData)$")
}
}
}
} catch {
print(error.localizedDescription)
}
}
}
}
task.resume()
}
Here is your func with the above code but as an NSDictionary. My console is showing all of the closes.
2017-10-24 CloseStock-> 256.5600
2018-02-22 CloseStock-> 270.4000
func fetchStockDataCalendar() {
let url = URL(string: "https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=\("SPY")&apikey=\(alphaApiKey)")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print ("ERROR")
} else {
if let content = data {
do {
let myJson = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
if let time = myJson["Time Series (Daily)"] as? NSDictionary {
for (key, value) in time {
if let value = value as? Dictionary<String, String> {
if let close = value["4. close"] {
print("\(key) CloseStock-> \(close)")
}
}
}
}
} catch {
print(error.localizedDescription)
}
}
}
}
task.resume()
}
Use a for loop to iterate over the dictionary keys and values like this:
if let time = myJson["Time Series (Daily)"] {
for (key, value) in time {
if let close = value["4. close"] {
print("\(key) CloseStock-> \(close)")
}
}
}
Based on your comments I've added this too
if let time = myJson["Time Series (Daily)"] {
for (key, value) in time {
if let value = value as? Dictionary<String, String> {
if let close = value["close"] {
print("\(key) CloseStock-> \(close)")
}
}
}
}

How to get values nested within dictionary in NSDictionary?

I apologise if the title is unclear. I am not quite sure how exactly NSDictionary and JSON works.
I am trying to make a simple app using Swift that takes values from a .JSON file, and prints them out to the console. Here is my JSON file:
{
"students": [
{
"name": "Stew Dent",
"age": "10",
"year": 6,
"grades": [
{
"english": "a-plus",
"math": "b-plus"
},
{
"english": "a",
"math": "a-minus"
}
]
},
{
"name": "Mary Brown",
"age": "14",
"year": 10,
"grades": [
{
"english": "a-plus",
"math": "a-plus"
},
{
"english": "a-plus",
"math": "a"
}
]
}
]
}
Currently, I am able to retrieve the values of everything except the values in "grades". Here is my Swift code:
if let path = Bundle.main.path(forResource: "school", ofType: "json")
{
do {
// Retrieve data from JSON file
let jsonData = try NSData(contentsOfFile: path, options: .mappedIfSafe)
// Convert data into NSDictionary
let jsonResult = try JSONSerialization.jsonObject(with: jsonData as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
if let students : [NSDictionary] = jsonResult?["students"] as? [NSDictionary]
{
if let grades : [NSDictionary] = students["grades"] as? [NSDictionary]
{
for grade: NSDictionary in grades
{
for (subject, gradevalue) in grade
{
print ("\(subject): \(gradevalue)")
}
}
}
}
} catch
{
}
}
If I am not wrong, the "grades" is a dictionary inside the "students" dictionary. However, it fails to run the above code. On the "if let grades..." line, it gives the error message: "Cannot subscript a value of type '[NSDictionary]' with an index type of 'String'.
So what I am I doing wrong? Is there even any way to retrieve those values? Am I missing something? Any help would be greatly appreciated. Thanks :)
You should use Swift native types Data instead of NSData and Dictionary [String:Any] instead of NSDictionary. You also forgot to iterate through each student. Try like this:
do {
if let dict = try JSONSerialization.jsonObject(with: jsonData) as? [String: Any] {
print(dict)
if let students = dict["students"] as? [[String: Any]] {
print(students)
for student in students {
print(student["name"] ?? "")
if let grades = student["grades"] as? [[String: Any]] {
for grade in grades {
for (subject, gradevalue) in grade {
print ("student:",student["name"] ?? "", "subject:", subject, "grade:", gradevalue)
}
}
}
}
}
}
} catch {
print(error)
}
Try this code, this should work looking at what you are trying to do
if let path = Bundle.main.path(forResource: "student", ofType: "json")
{
do {
// Retrieve data from JSON file
let jsonData = try NSData(contentsOfFile: path, options: .mappedIfSafe)
// Convert data into NSDictionary
let jsonResult = try JSONSerialization.jsonObject(with: jsonData as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
if let students = jsonResult?["students"] as? [NSDictionary]
{
for student in students
{
for grades in student["grades"] as! [[String:String]]{
for eachGrade in grades{
print("\(eachGrade.key) : \(eachGrade.value)")
}
}
}
}
}
catch
{
}
}