skip to Main Content

i have a very complicated issue for a beginner. firstly I have this result from json

{
  "success": true,
  "timeframe": true,
  "start_date": "2018-01-01",
  "end_date": "2018-01-05",
  "source": "TRY",
  "quotes": {
    "2018-01-01": {
      "TRYEUR": 0.21947
    },
    "2018-01-02": {
      "TRYEUR": 0.220076
    },
    "2018-01-03": {
      "TRYEUR": 0.220132
    },
    "2018-01-04": {
      "TRYEUR": 0.220902
    },
    "2018-01-05": {
      "TRYEUR": 0.222535
    }
  }
}

and when I use https://app.quicktype.io to create the object for me it gives this and that is right.

import Foundation

// MARK: - APIResult
struct APIResult {
    let success, timeframe: Bool
    let startDate, endDate, source: String
    let quotes: [String: Quote]
}

// MARK: - Quote
struct Quote {
    let tryeur: Double
}

but I don’t want my currencies hardcoded like this so if I choose from: USD to : EUR in my app I want to get the result under Quote as USDEUR. And I also know that if I change anything in this struct it won’t work. So how will make those currency selections dynamic to make it work in different currencies. This is a currency converter app and I want to get these rates and reflect it on a chart in my app. Thank you.

Edit: I think I need to get used to using stack overflow properly. Sorry for any inconvenience . At last I could get the dates and rates written in the console. my question is now :

how can i get these results in the console passed into my charts x(dates) and y axis(rates) ?

["2022-12-22": 19.803011, "2022-12-18": 19.734066, "2022-12-23": 19.907873, "2022-12-21": 19.79505, "2022-12-24": 19.912121, "2022-12-17": 19.756527, "2022-12-16": 19.752446, "2022-12-25": 19.912121, "2022-12-19": 19.794356, "2022-12-20": 19.824031]

this is the func i get these

func updateChart () {
    
    let date = Date()
    let endDate = formatter.string(from: date)
    let startDate = Calendar.current.date(byAdding: .day, value: -9, to: date)
    let startDatee = formatter.string(from: startDate ?? Date())
    print(endDate)
    print(startDatee)
    let result: () = currencyManager.fetchRatesForTimeframe(from: from, to: to, startDate: startDatee, endDate: endDate)
    print(result)
   }

and this is my previously created and hardcoded charts

override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        
        lineChart.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: 240)
        lineChart.center = view.center
        view.addSubview(lineChart)
       
       var entries = [ChartDataEntry]()
        
        for x in 0..<10 {
            
            entries.append(ChartDataEntry(x: Double(x), y: Double(x)))
        }
        
        let set = LineChartDataSet(entries: entries)
        
        set.colors = ChartColorTemplates.material()
        
        let data = LineChartData(dataSet: set)
        lineChart.data = data
    }

2

Answers


  1. Chosen as BEST ANSWER

    I changed my api provider now but this new one is not that much different than the previous one . this is the response I get inside browser

        {
      "success": true,
      "timeseries": true,
      "start_date": "2022-04-01",
      "end_date": "2022-04-05",
      "base": "USD",
      "rates": {
        "2022-04-01": {
          "TRY": 14.686504
        },
        "2022-04-02": {
          "TRY": 14.686504
        },
        "2022-04-03": {
          "TRY": 14.686145
        },
        "2022-04-04": {
          "TRY": 14.696501
        },
        "2022-04-05": {
          "TRY": 14.72297
        }
      }
    }
    

    this is my object

    struct APIResult: Codable {
        let timeseries: Bool
        let success: Bool
        let startDate: String
        let endDate: String
        let base: String
        var rates: [String:[String:Double]]
    }
    

    and this is my code inside VC to get the current date and 10 days before and I can see it printed in the console.

      lazy var formatter: DateFormatter = {
            let formatter = DateFormatter()
            formatter.timeZone = .current
            formatter.locale = .current
            formatter.dateFormat = "yyyy-MM-dd"
            return formatter
            
        }()
    

    func updateChart () {

    let date = Date()
    let endDate = formatter.string(from: date)
    let startDate = Calendar.current.date(byAdding: .day, value: -10, to: date)
    let startDatee = formatter.string(from: startDate ?? Date())
    print(endDate)
    print(startDatee)
    currencyManager.fetchRatesForTimeframe(from: from, to: to, startDate: startDatee, endDate: endDate)
    

    lastly these are the codes inside my other file called CurrencyManager

    func fetchRatesForTimeframe(from: String, to: String, startDate: String, endDate:String) {
            let urlString = "(timeFrameUrl)base=(from)&symbols=(to)&start_date=(startDate)&end_date=(endDate)&apikey=(api.convertApi)"
            performRequestforTimeframe(with: urlString)
      }
    
    func performRequestforTimeframe(with urlString: String) {
    
                    if let url = URL(string: urlString) {
    
                        let session = URLSession(configuration: .default)
    
                      let task = session.dataTask(with: url) { data, response, error in
    
                            if error != nil {
                                print(error!)
                            } else {
                                if let safeData = data {
    
                                   if let timeFrameRates = parseJSONForTimeframe(currencyData: safeData) {
                                       print(timeFrameRates)
                                       self.delegate?.didGetTimeframeRates(timeFrameRates)
                                    }
                                }
                            }
    
                        }
    
                        task.resume()
                    }
                }
    
        func cut(_ value: [String: [String: Double]]) -> [String: [String: Double]] {
            let dic = value
                .sorted(by: { $0.0 < $1.0 })[0..<10] // <-- last 10 results
                .reduce(into: [String: [String: Double]]()) {
                    $0[$1.key] = $1.value
                }
            return dic
        }
        
        func parseJSONForTimeframe(currencyData: Data) -> APIResult? {
    
            let decoder = JSONDecoder()
            let dateFormatter = DateFormatter()
            dateFormatter.dateFormat = "yyyy-MM-dd"
            decoder.dateDecodingStrategy = .formatted(dateFormatter)
            decoder.keyDecodingStrategy = .convertFromSnakeCase
    
            do {
                let decoder = JSONDecoder()
                var jsondata = try decoder.decode(APIResult.self, from: currencyData)
                jsondata.rates = cut(jsondata.rates)
        
                return jsondata
    
                } catch {
               
                return nil
            }
    
        }
    
    }
    

    why I can't get the result for print(timeFrameRates) inside func performRequestforTimeframe ? what s missing?


  2. Decodable is pretty versatile and highly customizable.

    Write a custom initializer and map the quote dictionary to an array of Quote instances which contains the date and the quote. The key TRYEUR is irrelevant and will be ignored.

    let jsonString = """
    {
      "success": true,
      "timeframe": true,
      "start_date": "2018-01-01",
      "end_date": "2018-01-05",
      "source": "TRY",
      "quotes": {
        "2018-01-01": {
          "TRYEUR": 0.21947
        },
        "2018-01-02": {
          "TRYEUR": 0.220076
        },
        "2018-01-03": {
          "TRYEUR": 0.220132
        },
        "2018-01-04": {
          "TRYEUR": 0.220902
        },
        "2018-01-05": {
          "TRYEUR": 0.222535
        }
      }
    }
    """
    
    struct APIResult: Decodable {
        private enum CodingKeys: String, CodingKey {
            case success, timeframe, startDate = "start_date", endDate = "end_date", source, quotes
        }
        let success, timeframe: Bool
        let startDate, endDate, source: String
        let quotes: [Quote]
        
        init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            success = try container.decode(Bool.self, forKey: .success)
            timeframe = try container.decode(Bool.self, forKey: .timeframe)
            startDate = try container.decode(String.self, forKey: .startDate)
            endDate = try container.decode(String.self, forKey: .endDate)
            source =  try container.decode(String.self, forKey: .source)
            let quoteData = try container.decode([String: [String:Double]].self, forKey: .quotes)
            quotes = quoteData.compactMap({ (key, value) in
                guard let quote = value.values.first else { return nil }
                return Quote(date: key, quote: quote)
            }).sorted{$0.date < $1.date}
        }
    }
    
    struct Quote {
        let date: String
        let quote: Double
    }
    
    
    do {
        let result = try  JSONDecoder().decode(APIResult.self, from: Data(jsonString.utf8))
        print(result)
    } catch {
        print(error)
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search