i have the following code in which i'm trying to download exchange rates into my app to use in currency conversion. The data fetch seems to work ok, as does the json decoding model, but i'm unable to get the data through the Rates variable
import SwiftUI
struct ExchangeRates: Codable {
var conversionRates: [String: Double]?
init(conversionRates:[String:Double]) {
self.conversionRates = conversionRates
}
enum CodingKeys: String, CodingKey {
case conversionRates = "conversion_rates"
}
}
class DownloadingData:ObservableObject{
@Published var Rates:ExchangeRates = ExchangeRates.init(conversionRates: ["test" : 0])
init() {
datatask()
}
func datatask() {
guard let url = URL(string: "https://v6.exchangerate-api.com/v6/********************/latest/GBP") else {return}
URLSession.shared.dataTask(with: url){(data,response,error) in
guard let data = data else {
print("no data")
return
}
guard error == nil else {
print("error :\(String(describing: error))")
return
}
guard let response = response as? HTTPURLResponse else {
print("invalid response")
return
}
guard response.statusCode >= 200 && response.statusCode < 300 else {
print("status code should be 2xx, but is \(response.statusCode)")
return
}
guard let rates = try? JSONDecoder().decode(ExchangeRates.self, from: data) else {return}
print(rates) **// this works and prints out data**
DispatchQueue.main.async {
[weak self] in
self?.Rates = rates
print(self?.Rates) **// this works and prints out the data**
}
print(self.Rates) **// this doesnt print out anything**
}.resume()
print(Rates) **// this doesnt print out anything**
}
}
i can't seem to get the data into the Rates Variable any guidance please
thanks
here is a sample of the console output :
ExchangeRates(conversionRates: Optional(["test": 0.0])) Optional(test3.ExchangeRates(conversionRates: Optional(["BZD": 2.7002, "PHP": 68.7948, "PGK": 4.725, "BND": 1.8176, "HNL": 32.8885, "TND": 3.7553, "BDT": 115.2218, "SBD": 10.6866, "NIO": 47.4824, "XDR": 0.963, "IDR": 19213.9064, "XCD": 3.6453, "CAD": 1.7152, "UGX": 4778.6135,])
CodePudding user response:
Declare rates
– please with starting lowercase letter – as empty dictionary
@Published var rates = [String:Double]()
In the struct delete the init method and declare the dictionary also non-optional
struct ExchangeRates: Decodable {
let conversionRates: [String: Double]
enum CodingKeys: String, CodingKey {
case conversionRates = "conversion_rates"
}
}
in the DispatchQueue
closure assign the value of conversionRates
to rates
DispatchQueue.main.async { // no weak self needed
self.rates = rates.conversionRates
}
In the view enumerate the dictionary rates
CodePudding user response:
you could try this, using your ExchangeRates
struct:
class DownloadingData: ObservableObject{
@Published var rates = ExchangeRates(conversionRates: ["test" : 0])
init() {
datatask()
}
func datatask() {
guard let url = URL(string: "https://v6.exchangerate-api.com/v6/********************/latest/GBP") else {return}
URLSession.shared.dataTask(with: url){(data,response,error) in
guard let data = data else {
print("no data")
return
}
guard error == nil else {
print("error :\(error)")
return
}
guard let response = response as? HTTPURLResponse else {
print("invalid response")
return
}
guard response.statusCode >= 200 && response.statusCode < 300 else {
print("status code should be 2xx, but is \(response.statusCode)")
return
}
guard let exRates = try? JSONDecoder().decode(ExchangeRates.self, from: data) else {return}
print(exRates)
DispatchQueue.main.async {
self.rates = exRates // <--- here
print(self.rates) // <--- here
}
// print(self.rates) // <--- NEVER here
}.resume()
}
}
EDIT-1: with completion closure:
class DownloadingData: ObservableObject{
@Published var rates = ExchangeRates(conversionRates: ["test" : 0])
init() {
datatask() { isDone in
print(self.rates) // <--- here OK
}
}
func datatask(completion: @escaping(Bool) -> ()) { // <--- here
guard let url = URL(string: "https://v6.exchangerate-api.com/v6/********************/latest/GBP") else {return}
URLSession.shared.dataTask(with: url){(data,response,error) in
guard let data = data else {
print("no data")
return
}
guard error == nil else {
print("error :\(error)")
return
}
guard let response = response as? HTTPURLResponse else {
print("invalid response")
return
}
guard response.statusCode >= 200 && response.statusCode < 300 else {
print("status code should be 2xx, but is \(response.statusCode)")
return
}
guard let exRates = try? JSONDecoder().decode(ExchangeRates.self, from: data) else {return}
print(exRates) // <--- here OK
DispatchQueue.main.async {
self.rates = exRates
print(self.rates) // <--- here OK
completion(true) // <--- here return completion
}
}.resume()
}
}