Search code examples
variablesswiftuidatamodeljsondecoder

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


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,])


Solution

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