【问题标题】:Dynamically Changing the Base Currency动态改变基础货币
【发布时间】:2021-04-23 22:54:05
【问题描述】:

我有一个货币 API,它返回一个包含奇怪排列的 JSON 对象:基础货币用作标签。典型的货币 API 具有“base”、“date”、“success”和“rates”等标签,但此 API 没有这些标签。

{
    "usd": {
        "aed": 4.420217,
        "afn": 93.3213,
        "all": 123.104693,
        "amd": 628.026474,
        "ang": 2.159569,
        "aoa": 791.552347,
        "ars": 111.887966,
        "aud": 1.558363,
        "awg": 2.164862,
        "azn": 2.045728,
        "bam": 1.9541,
        "bbd": 2.429065,
        "bch": 0.001278
    }
}

顶部的“usd”(美元)称为基础货币或本国货币。目前,存储结构和状态参数被硬编码为“usd”,这防止使用与其他基础货币的汇率。汇率 API 适用于美元基础货币。

我需要帮助进行修改,以便下载不同基础货币的汇率。例如,我可以在存储模型和状态参数中使用字符串变量吗?任何启发将不胜感激。

struct RateResult: Codable {
        let usd: [String: Double]
    }    

   @State private var  results = RateResult(usd: [:])


struct ContentView: View {

   var body: some View {

   }

   func UpdateRates() {

      let baseUrl = "https://cdn.jsdelivr.net/gh/fawazahmed0/currency-api@1/latest/currencies/"
        let baseCur = baseCurrency.baseCur.baseS   // usd
        let requestType = ".json"
        
        guard let url = URL(string: baseUrl + baseCur + requestType) else {
            print("Invalid URL")
            return
        }
        let request = URLRequest(url: url)
        URLSession.shared.dataTask(with: request) { data, response, error in
            if let data = data {
                if let decodedResponse = try? JSONDecoder().decode(RateResult.self, from: data) {
                    
                    // we have good data – go back to the main thread
                    DispatchQueue.main.async {
                        
                        // update our UI
                        self.results = decodedResponse
                        
                    // save off currency exchange rates
                    
                    }
                    // everything is good, so we can exit
                    return
                }
            }
            // if we're still here it means there was a problem
            print("Currency Fetch Failed: \(error?.localizedDescription ?? "Unknown error")")
        }.resume()
    }
}

【问题讨论】:

  • 很难理解这个问题是关于什么的,它真的是一个 SwiftUI 问题还是关于如何处理你的数据结构甚至如何下载?

标签: swiftui ios14


【解决方案1】:
import SwiftUI
//You can't use the standard Codable for this. You have to make your own.
class BaseCurrency: Codable {
    let id = UUID()
    var baseCurrencies: [String : [String: Double]] = [:]
    required public init(from decoder: Decoder) throws {
        do{
            print(#function)
            let baseContainer = try decoder.singleValueContainer()
            let base = try baseContainer.decode([String : [String: Double]].self)
            for key in base.keys{
                baseCurrencies[key] = base[key]
            }
        }catch{
            print(error)
            throw error
        }
    }
    //@State should never be used outside a struct that is a View
}
struct CurrencyView: View {
    @StateObject var vm: CurrencyViewModel = CurrencyViewModel()
    
    var body: some View {
        VStack{
            List{
                if vm.results != nil{
                    ForEach(vm.results!.baseCurrencies.sorted{$0.key < $1.key}, id: \.key) { key, baseCurrency in
                        DisclosureGroup(key){
                            ForEach(baseCurrency.sorted{$0.key < $1.key}, id: \.key) { key, rate in
                                HStack{
                                    Text(key)
                                    Text(rate.description)
                                }
                            }
                        }
                    }
                }else{
                    Text("waiting...")
                }
            }
            //To select another rate to go fetch
            RatesPickerView().environmentObject(vm)
        }.onAppear(){
            vm.UpdateRates()
        }
    }
}
struct RatesPickerView: View {
    @EnvironmentObject var vm: CurrencyViewModel
    var body: some View {
        if vm.results != nil{
            //You can probaly populate this picker with the keys in
            // baseCurrency.baseCur.baseS
            Picker("rates", selection: $vm.selectedBaseCurrency){
                ForEach((vm.results!.baseCurrencies.first?.value.sorted{$0.key < $1.key})!, id: \.key) { key, rate in
                    Text(key).tag(key)
                }
            }
        }else{
            Text("waiting...")
        }
    }
}
class CurrencyViewModel: ObservableObject{
    
    @Published var results: BaseCurrency?
    @Published var selectedBaseCurrency: String = "usd"{
        didSet{
            UpdateRates()
        }
    }
    init() {
        //If you can .onAppear you don't need it here
        //UpdateRates()
    }
    func UpdateRates() {
        print(#function)
        let baseUrl = "https://cdn.jsdelivr.net/gh/fawazahmed0/currency-api@1/latest/currencies/"
        let baseCur = selectedBaseCurrency   // usd
        let requestType = ".json"
        
        guard let url = URL(string: baseUrl + baseCur + requestType) else {
            print("Invalid URL")
            return
        }
        let request = URLRequest(url: url)
        URLSession.shared.dataTask(with: request) { data, response, error in
            if let data = data {
                do{
                    let decodedResponse = try JSONDecoder().decode(BaseCurrency.self, from: data)
                    
                    DispatchQueue.main.async {
                        
                        if self.results == nil{
                            //Assign a new base currency
                            self.results = decodedResponse
                        }else{ //merge the existing with the new result
                            for base in decodedResponse.baseCurrencies.keys{
                                self.results?.baseCurrencies[base] = decodedResponse.baseCurrencies[base]
                            }
                        }
                        //update the UI
                        self.objectWillChange.send()
                    }
                    
                }catch{
                    //Error thrown by a try
                    print(error)//much more informative than error?.localizedDescription
                }
            }
            if error != nil{
                //data task error
                print(error!)
            }
        }.resume()
    }
}
struct CurrencyView_Previews: PreviewProvider {
    static var previews: some View {
        CurrencyView()
    }
}

【讨论】:

  • 感谢 lorem ipsum 花时间研究这个问题。这比我最初想象的更具挑战性!我想从 onAppear (.onAppear(perform: CurrencyViewModel.UpdateRates()) 调用 UpdateRates() 方法,但我收到错误“调用中的参数 #1 缺少参数。修复:插入 ' '。初始化逻辑没有参数。必须有一种更简单的方法,但我从主选项卡视图调用此货币逻辑,然后在每天多次更新货币汇率之前检查一些时钟逻辑。跨度>
  • 不看代码就不可能知道发生了什么(.onAppear(perform: CurrencyViewModel.UpdateRates()) 虽然这看起来是错误的。开头的那个左括号是粗略的,您将它作为参数传递给某些东西,UpdateRates 不返回任何东西。此外,CurrencyViewModel.UpdateRates() 被称为错误。根据我的代码,这不是static 方法。
  • 我提供的代码可以独立运行。如果您将它放入一个空的项目/swift 文件并在画布或设备上运行它,您将看到它是如何联系在一起的。当移动到新货币时,选择器会触发一次提取,并将其添加到已经提取的货币中。
  • 我在CurrencyView 中添加了一个onAppear() 调用,以便您了解它是如何工作的。
  • 好的,我通过对主选项卡视图的这些更改运行它:@StateObject var vm: CurrencyViewModel = CurrencyViewModel() 并像这样调用 UpdateRates:.onAppear(perform: vm.UpdateRates)
猜你喜欢
  • 1970-01-01
  • 2015-11-20
  • 1970-01-01
  • 2014-08-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-18
相关资源
最近更新 更多