Home > OS >  im having difficulty getting downloaded json response into my data model and accessing it in code
im having difficulty getting downloaded json response into my data model and accessing it in code

Time:01-03

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()
    }
}
  • Related