Reputation: 3498
I am trying to access fixer.io by making an API call. It is the first time than I am trying to do so, but I don't get the result wanted. I would like to get the "rate" and the "result" from this JSON file.
{
"success": true,
"query": {
"from": "GBP",
"to": "JPY",
"amount": 25
},
"info": {
"timestamp": 1519328414,
"rate": 148.972231
},
"historical": ""
"date": "2018-02-22"
"result": 3724.305775
}
The method that I have implemented is this one, but I can not figure out how to retrieve "rate" and "result" when making this API call.
extension APIsRuler {
func getExchangeRate(from: String, to: String, amount: String, callback: @escaping (Bool, ConversionResult?) -> Void) {
var request = URLRequest(url: APIsRuler.exchangeURL)
let body = "convert?access_key=\(APIsRuler.exchangeAPI)&from=\(from)&to=\(to)&amount=\(amount)"
request.httpMethod = "GET"
request.httpBody = body.data(using: .utf8)
let session = URLSession(configuration: .default)
task?.cancel()
task = session.dataTask(with: request) { (data, response, error) in
DispatchQueue.main.async {
guard let data = data, error == nil else {
return callback(false, nil)
}
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
return callback(false, nil)
}
guard let responseJSON = try? JSONDecoder().decode([String: Double].self, from: data),
let rate = responseJSON["rate"],
let result = responseJSON["result"] else {
return callback(false, nil)
}
let conversionResult = ConversionResult(exchangeRate: rate, exchangeResult: result)
callback(true, conversionResult)
}
}
task?.resume()
}
}
Upvotes: 0
Views: 69
Reputation: 285072
You are mixing up two different APIs.
Either use JSONSerialization
, the result is a dictionary and you get the values by key and index subscription. And you have to downcast every type and consider the nested rate
value.
guard let responseJSON = try? JSONSerialization.jsonObject(with: data) as? [String:Any],
let info = responseJSON["info"] as? [String:Any],
let rate = info["rate"] as? Double,
let result = responseJSON["result"] as? Double else {
return callback(false, nil)
}
let conversionResult = ConversionResult(exchangeRate: rate, exchangeResult: result)
callback(true, conversionResult)
Or use JSONDecoder
then you have to create structs, decoding to [String:Double]
can work only if all values in the root object are Double
which is clearly not the case.
struct Root: Decodable {
let info: Info
let result: Double
}
struct Info: Decodable {
let rate: Double
}
guard let responseJSON = try? JSONDecoder().decode(Root.self, from: data) else {
return callback(false, nil)
}
let conversionResult = ConversionResult(exchangeRate: responseJSON.info.rate, exchangeResult: responseJSON.result)
callback(true, conversionResult)
The code is only an example to keep your syntax. Practically you are strongly discouraged from using try?
when decoding JSON. Always catch
and handle errors
do {
let responseJSON = try JSONDecoder().decode(Root.self, from: data)
let conversionResult = ConversionResult(exchangeRate: responseJSON.info.rate, exchangeResult: responseJSON.result)
callback(true, conversionResult)
} catch {
print(error)
return callback(false, nil)
}
Upvotes: 0
Reputation: 17863
Use a real model object, like this:
struct Conversion: Codable {
let success: Bool
let query: Query
let info: Info
let historical, date: String
let result: Double
}
struct Info: Codable {
let timestamp: Int
let rate: Double
}
struct Query: Codable {
let from, to: String
let amount: Int
}
and parse your response into it using JSONDecoder
:
do {
let conversion = try JSONDecoder().decode(Conversion.self, from: data)
let rate = conversion.info.rate
let result = conversion.result
} catch { print(error) }
Upvotes: 1