I am going to refer you to this answer for a more detailed description. I will fully admit to stealing the work of @nicksarno in crafting this answer. Please read the entire answer as he and I give this issue a pretty thorough response. I would have just referred you there, but there is a lot to unpack here in addition.
First, please make sure you change your API key. You should not EVER post a private API key. You pay (or will pay) for access to that data.
On to the answer. Your request code was incorrect. What I have posted is a modern request using Combine that is fully discussed in the answer link I posted. In addition, I corrected your decoder by simply running it through QuickType
, which is also referenced in the answer.
Parsing JSON is a pain, and if nothing else, start with Quicktype
or some other parser. In this case, the JSON data, even though you only requested one stock, was an array. You decoder has to exactly match the structure of the JSON, though it does not need to have every element of the JSON. It also has to conform to the Codable
protocol.
import SwiftUI
struct Helper: View {
@State private var symbol: String?
var body: some View {
HStack {
Spacer()
VStack(alignment: .leading) {
Text(symbol ?? ".")
Button(action: { loadData() }) {
Text("Press here")
}
}
}
}
private func loadData() {
guard let url = URL(string: "https://financialmodelingprep.com/api/v3/quote/AAPL?apikey=836129f3de1b99ff975e910a4541254d") else {
return
}
URLSession.shared.dataTaskPublisher(for: url)
// fetch on background thread
.subscribe(on: DispatchQueue.global(qos: .background))
// recieve response on main thread
.receive(on: DispatchQueue.main)
// ensure there is data
.tryMap { (data, response) in
guard
let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw URLError(.badServerResponse)
}
return data
}
// decode JSON data to QuoteDecoder
.decode(type: QuoteDecoder.self, decoder: JSONDecoder())
// Handle results
.sink { (result) in
// will return success or failure
print("completion: (result)")
} receiveValue: { (value) in
// if success, will return QuoteDecoder
// here you can update your view
print("value: (value)")
// you can handle your data however you need to. Remember that it is an array of QuoteData.
if let quote = value.first {
self.symbol = quote.symbol
}
}
// After recieving response, the URLSession is no longer needed & we can cancel the publisher
.cancel()
}
}
struct QuoteData: Codable {
let symbol, name: String?
let price, changesPercentage, change, dayLow: Double?
let dayHigh, yearHigh, yearLow: Double?
let marketCap: Int?
let priceAvg50, priceAvg200: Double?
let volume, avgVolume: Int?
let exchange: String?
let quoteDatumOpen, previousClose, eps, pe: Double?
let earningsAnnouncement: String?
let sharesOutstanding, timestamp: Int?
enum CodingKeys: String, CodingKey {
case symbol, name, price, changesPercentage, change, dayLow, dayHigh, yearHigh, yearLow, marketCap, priceAvg50, priceAvg200, volume, avgVolume, exchange
case quoteDatumOpen = "open"
case previousClose, eps, pe, earningsAnnouncement, sharesOutstanding, timestamp
}
}
typealias QuoteDecoder = [QuoteData]
struct Helper_Previews: PreviewProvider {
static var previews: some View {
Helper()
}
}