I've been struggling with this one! I've got Alamofire and SwiftyJSON. I use Alamofire to get a JSON result from Yahoo Finance like this:
public func getYahooQuote(symbol: String) {
let stockURL = "https://query1.finance.yahoo.com/v7/finance/quote?symbols=" + symbol
let request = AF.request(stockURL, parameters: ["quoteResponse": "result"])
request.responseData { (response) in
guard let data = response.value else {return}
do {
let json = try JSON(data: data)
print(json)
let decoder = JSONDecoder()
let stock = try decoder.decode(QuoteParent.self, from: data)
print(stock)
} catch {
print(error)
}
}
}
So that request takes a string variable symbol which is passed into the function. The result I get is a JSON object that prints this: '
{
"quoteResponse" : {
"result" : [
{
"fiftyTwoWeekLow" : 164.93000000000001,
"regularMarketVolume" : 33445281,
"messageBoardId" : "finmb_8108558",
"symbol" : "QQQ",
"currency" : "USD",
"regularMarketPreviousClose" : 258.00999999999999,
"fiftyDayAverage" : 250.32285999999999,
"exchange" : "NMS",
"quoteType" : "ETF",
"regularMarketDayLow" : 251.31999999999999,
"averageDailyVolume10Day" : 46768962,
"fiftyTwoWeekHighChange" : -15.310013,
"priceHint" : 2,
"twoHundredDayAverageChange" : 31.669998,
"exchangeTimezoneName" : "America\/New_York",
"bookValue" : 188.77500000000001,
"firstTradeDateMilliseconds" : 921076200000,
"averageDailyVolume3Month" : 42292663,
"tradeable" : false,
"bidSize" : 8,
"sourceInterval" : 15,
"regularMarketChange" : -3.530014,
"triggerable" : true,
"longName" : "Invesco QQQ Trust",
"market" : "us_market",
"exchangeTimezoneShortName" : "EDT",
"regularMarketDayHigh" : 256.93000000000001,
"marketCap" : 100036083712,
"gmtOffSetMilliseconds" : -14400000,
"fiftyTwoWeekHighChangePercent" : -0.056747886999999997,
"askSize" : 10,
"language" : "en-US",
"marketState" : "REGULAR",
"fiftyTwoWeekRange" : "164.93 - 269.79",
"twoHundredDayAverage" : 222.81,
"trailingAnnualDividendRate" : 1.54,
"quoteSourceName" : "Delayed Quote",
"trailingThreeMonthReturns" : 30.27,
"fiftyDayAverageChange" : 4.1571350000000002,
"shortName" : "Invesco QQQ Trust, Series 1",
"fiftyDayAverageChangePercent" : 0.016607093,
"region" : "US",
"regularMarketTime" : 1595609084,
"priceToBook" : 1.3480599,
"regularMarketOpen" : 254.12,
"fiftyTwoWeekLowChange" : 89.549999999999997,
"regularMarketDayRange" : "251.32 - 256.93",
"trailingAnnualDividendYield" : 0.0059687606999999998,
"fullExchangeName" : "NasdaqGS",
"regularMarketChangePercent" : -1.3681694,
"trailingPE" : 65.335044999999994,
"fiftyTwoWeekHigh" : 269.79000000000002,
"bid" : 254.56,
"epsTrailingTwelveMonths" : 3.895,
"trailingThreeMonthNavReturns" : 30.210000000000001,
"fiftyTwoWeekLowChangePercent" : 0.54295766000000001,
"twoHundredDayAverageChangePercent" : 0.14213903,
"ask" : 254.61000000000001,
"esgPopulated" : false,
"regularMarketPrice" : 254.47999999999999,
"sharesOutstanding" : 393100000,
"financialCurrency" : "USD",
"exchangeDataDelayedBy" : 0,
"ytdReturn" : 16.809999999999999
}
],
"error" : null
}
}
I've got Codable structs like this:
struct QuoteParent: Codable {
var quoteResponse: QuoteResponse
}
struct QuoteResponse: Codable {
var error: QuoteError?
var result: Stock?
}
struct QuoteError: Codable {
var lang: String?
var description: String?
var message: String?
var code: Int
}
struct Stock: Codable {
var ask : Decimal
var askSize : Int
var averageDailyVolume10Day : Int
var averageDailyVolume3Month : Int
var bid : Double
var bidSize : Int
var bookValue : Decimal
var currency : String
var epsTrailingTwelveMonths : Decimal
var esgPopulated : Bool
var exchange : String
var exchangeDataDelayedBy : Int
var exchangeTimezoneName : String
var exchangeTimezoneShortName : String
var fiftyDayAverage : Decimal
var fiftyDayAverageChange : Decimal
var fiftyDayAverageChangePercent : Decimal
var fiftyTwoWeekHigh : Decimal
var fiftyTwoWeekHighChange : Decimal
var fiftyTwoWeekHighChangePercent : Decimal
var fiftyTwoWeekLow : Decimal
var fiftyTwoWeekLowChange : Decimal
var fiftyTwoWeekLowChangePercent : Decimal
var fiftyTwoWeekRange : String?
var financialCurrency : String
var firstTradeDateMilliseconds : Int
var fullExchangeName : String
var gmtOffSetMilliseconds : Int
var language : String
var longName : String
var market : String
var marketCap : Int
var marketState : String
var messageBoardId : String
var priceHint : Int
var priceToBook : Decimal
var quoteSourceName : String
var quoteType : String
var region : String
var regularMarketChange : Int
var regularMarketChangePercent : Decimal
var regularMarketDayHigh : Decimal
var regularMarketDayLow : Decimal
var regularMarketDayRange : String
var regularMarketOpen : Double
var regularMarketPreviousClose : Decimal
var regularMarketPrice : Decimal
var regularMarketTime : Int
var regularMarketVolume : Int
var sharesOutstanding : Int
var shortName : String
var sourceInterval : Int
var symbol : String
var tradeable : Bool
var trailingAnnualDividendRate : Double
var trailingAnnualDividendYield : Decimal
var trailingPE : Decimal
var trailingThreeMonthNavReturns : Decimal
var trailingThreeMonthReturns : Decimal
var triggerable : Bool
var twoHundredDayAverage : Double
var twoHundredDayAverageChange : Decimal
var twoHundredDayAverageChangePercent : Decimal
var ytdReturn : Decimal
}
I've tried to decode that using JSONDecoder, but that seems to need a Data object, while the object I get is JSON.
I use this line to narrow the JSON object to just the value of result like this:
let json2 = json["quoteResponse"]["result"]
Now that's still just a JSON object, which does contain all the data I want, but I have not been able to figure out how to parse that JSON object to the Struct class I have. Any wisdom here would be so appreciated!
I did try this to get the JSON:
request.responseData { (response) in
instead of
request.responseJSON { (response) in
And attempted to decode it with:
let decoder = JSONDecoder()
let stock = try decoder.decode(Stock.self, from: data)
But now the error I get prints like this:
typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "quoteResponse", intValue: nil), CodingKeys(stringValue: "result", intValue: nil)], debugDescription: "Expected to decode Dictionary<String, Any> but found an array instead.", underlyingError: nil))
String
, floating point values areDouble
, the other areInt
,true
andfalse
(not in double quotes) areBool
. AndCodable
is much better thanSwiftyJSON
. Declare everything non-optional, read the comprehensive errors you get and fix the issues. – vadian.responseJSON
with.responseData
– vadianprint(error)
in thecatch
block rather than anything else. It tells you what's wrong. Hint: The root object (with thequoteResponse
key) is missing. – vadian