I'm trying to parse stock data from the website alphavantage.com using the combine frame work. I keep getting this error Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"bestMatches\", intValue: nil) (\"bestMatches\").", underlyingError: nil))
despite my data model having the correct values to match during with the json. How do I fix this ?
struct SearchResults: Decodable{
let bestMatches : [SearchResult]
enum CodingKeys: String, CodingKey{
case bestMatches = "bestMatches"
}
}
struct SearchResult : Decodable{
let symbol : String?
let name : String?
let type : String?
let currency :String?
enum CodingKeys:String, CodingKey{
case symbol = "1. symbol"
case name = "2. name"
case type = "3. type"
case currency = "8. currency"
}
}
struct APIservice{
let apiKey = "U893NJLDIREGERHB"
func fetchSymbols(keyword:String)-> AnyPublisher<SearchResults,Error>{
let urlSTring = "https://www.alphavantage.co/query?function=\(keyword)H&keywords=tesco&apikey=U893NJLDIREGERHB"
let url = URL(string: urlSTring)!
return URLSession.shared.dataTaskPublisher(for: url)
.map({$0.data})
.decode(type: SearchResults.self, decoder: JSONDecoder())
.receive(on: RunLoop.main)
.eraseToAnyPublisher()
}
}
func performSearch(){
apiSerivice.fetchSymbols(keyword: "S&P500").sink { (completion) in
switch completion {
case .failure(let error):
print(error)
case . finished:
break
}
} receiveValue: { (SearchResults) in
print(SearchResults.bestMatches)
}.store(in: &subcribers)
CodePudding user response:
Your query is incorrect. Checking the documentation for symbol search, your URL must pass in SYMBOL_SEARCH
for the function
query.
Your query for the keyword was also not URL encoded as it should be, so "S&P 500"
had the issue that it creates an invalid query when string interpolated. The better way would be to use URLComponents
so this is handled for you safely.
Code:
func fetchSymbols(keyword: String) -> AnyPublisher<SearchResults, Error> {
guard var components = URLComponents(string: "https://www.alphavantage.co/query") else {
return Fail(
outputType: SearchResults.self,
failure: APIError.invalidComponents
).eraseToAnyPublisher()
}
components.queryItems = [
URLQueryItem(name: "function", value: "SYMBOL_SEARCH"),
URLQueryItem(name: "keywords", value: keyword),
URLQueryItem(name: "apikey", value: apiKey)
]
guard let url = components.url else {
return Fail(
outputType: SearchResults.self,
failure: APIError.invalidURL
).eraseToAnyPublisher()
}
return URLSession.shared.dataTaskPublisher(for: url)
.map(\.data)
.decode(type: SearchResults.self, decoder: JSONDecoder())
.receive(on: RunLoop.main)
.eraseToAnyPublisher()
}
enum APIError: Error {
case invalidComponents
case invalidURL
}
I changed the query from "S&P500"
to "S&P 500"
because otherwise there are no results. You can also remove the redundant CodingKeys
in SearchResults
too because that has no effect.
Note: do not expose your API key!