We are new programmers and are trying to parse an Exchange Rate API. We are able to pull data from the API, but are unable to extract and use that data.
Line 54 shows the API is working, and will show the correct data in the debug area.
Line 58 is, we believe, the problem area.
Any suggestions are appreciated.
Kind Regards,
import UIKit
struct ExchangeRates: Decodable {
var base: String = ""
var rates: [String: Double]
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
getExRate(currencyBase: "USD")
//this whole for-in loop did not work
for (key, value) in updatedExRates {
// var exchangeRateValue: Double
if (key == "HUF"){
// exchangeRateValue = value
print(value)
displayLabel.text! = "\(key): \(value)"
}
}
}
@IBOutlet weak var displayLabel: UILabel!
var updatedExRates = [String: Double]()
func getExRate(currencyBase: String) {
// ((completion: @escaping(Result<exchangerates, currencyerror="">) -> Void))
let resourceString = "https://api.exchangeratesapi.io/latest?base=\(currencyBase)"
let resourceURL = URL(string: resourceString)
print(resourceURL!)
guard resourceURL != nil else{
return
}
let session = URLSession.shared
let dataTask = session.dataTask(with: resourceURL!) { (data, response, error) in
//check for error
if error == nil && data != nil {
//parse JSON
let decoder = JSONDecoder()
do{
let exRates = try decoder.decode(ExchangeRates.self, from: data!)
print(exRates) //this works(see debug area)
// this is where the issue started to occur.
//It does not assign the dictionary to this variable I have created(returns nil instead).
self.updatedExRates = exRates.rates
}
catch {
print("error in JSON parsing")
}
}
}
//Make the API call
dataTask.resume()
}
}
Please post what you get on logs from print.
And add this print, to tell what you get as well:
print("Dictionary", updatedExRates)
for (key, value) in updatedExRates {
// var exchangeRateValue: Double
if (key == "USD"){
let exchangeRateValue = value
print(value)
displayLabel.text! = String(exchangeRateValue)
}
}
Probably, at this stage, dict is empty.
The problem is probably an async problem.
Here is what happens in the following code (I reformat a little to get declarations at top (just for easier reading):
class ViewController: UIViewController {
@IBOutlet weak var displayLabel: UILabel!
var updatedExRates = [String: Double]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
getExRate(currencyBase: "USD")
//this whole for-in loop did not work
for (key, value) in updatedExRates {
// var exchangeRateValue: Double
if (key == "HUF"){
// exchangeRateValue = value
print(value)
displayLabel.text! = "\(key): \(value)"
}
}
}
func getExRate(currencyBase: String) {
- Line 11, you get the exchange rate and load updatedExRates.
- This loading (in getExRate) is executed in another thread for session.dataTask
- So, line 11 calls the func ; a new thread is opened for session.dataTask
- But, before completion, viewDidload continues to line 14 ;
- Thus, updatedExRates is not yet loaded, it is still nil.
A way to solve it is to call the label update after task has completed.
Create a func
func updateLabel() {
for (key, value) in updatedExRates {
// var exchangeRateValue: Double
if (key == "HUF"){
// exchangeRateValue = value
print(value)
DispatchQueue.main.async { // In case we're called from another thread
displayLabel.text! = "\(key): \(value)"
}
}
}
}
Remove this part from viewDidLoad and add it in getExRate:
let dataTask = session.dataTask(with: resourceURL!) { (data, response, error) in
//check for error
if error == nil && data != nil {
//parse JSON
let decoder = JSONDecoder()
do{
let exRates = try decoder.decode(ExchangeRates.self, from: data!)
// this is where the issue started to occur.
//It does not assign the dictionary to this variable I have created(returns nil instead).
self.updatedExRates = exRates.rates
updateLabel() // Update will be done in main thread
}
catch {
print("error in JSON parsing")
}
}
}
dataTask.resume()
}