从 SwiftUI 中的 URL 获取 JSON

Posted

技术标签:

【中文标题】从 SwiftUI 中的 URL 获取 JSON【英文标题】:Fetching JSON from URL in SwiftUI 【发布时间】:2020-07-21 16:13:34 【问题描述】:

以下是我要开始工作的代码,我要做的就是显示当前价格和它所在的货币。

链接返回格式为: "currency":"USD","rate":"178.0466666666667"

import SwiftUI
import Combine

struct BitcoinPrice: Decodable, Identifiable 
    var id = UUID()
    let currency: String
    let rate: String



struct BSVPrice: View 
    @State private var requests = Set<AnyCancellable>()
    @State private var exchangeRate = [BitcoinPrice]()
    
    var body: some View 
        NavigationView 
            List(exchangeRate)  rate in
                VStack(alignment: .leading) 
                    Text(rate.currency)
                    Text(rate.rate)
                
            
            .navigationTitle("Bitcoin Price")
        
        
        .onAppear 
            let bitcoinPriceURL = URL(string: "https://api.whatsonchain.com/v1/bsv/main/exchangerate")!
            //let bitcoinPriceTask = fetch(bitcoinPriceURL, defaultValue: [BitcoinPrice]() )
            fetch(bitcoinPriceURL, defaultValue: [BitcoinPrice]() ) 
                exchangeRate = $0
            
        
    
    
    func fetch<T: Decodable>(_ url: URL, defaultValue: T, completion: @escaping (T) -> Void) 
        let decoder = JSONDecoder()
        
        URLSession.shared.dataTaskPublisher(for: url)
            .retry(1)
            .map(\.data)
            .decode(type: T.self, decoder: decoder)
            .replaceError(with: defaultValue)
            .receive(on: DispatchQueue.main)
            .sink(receiveValue: completion)
            .store(in: &requests)
    
    
    func fetch<T: Decodable>(_ url: URL, defaultValue: T) -> AnyPublisher<T, Never> 
        let decoder = JSONDecoder()
        
        return URLSession.shared.dataTaskPublisher(for: url)
            .retry(1)
            .map(\.data)
            .decode(type: T.self, decoder: decoder)
            .replaceError(with: defaultValue)
            .receive(on: DispatchQueue.main)
            .eraseToAnyPublisher()
    



struct BSVPrice_Previews: PreviewProvider 
    static var previews: some View 
        BSVPrice()
    

有谁知道为什么 JSON 没有加载到应用程序中?任何帮助都会很棒,我已经让代码与不同的 API 一起工作,所以有点困惑。

【问题讨论】:

您应该以某种方式获取并打印解码错误。有一个错误。 【参考方案1】:

首先,您通过使用.replaceError(with: defaultValue) 来抑制错误,这将隐藏所有错误并将其替换为默认值(此处为空列表)。


func fetch<T: Decodable>(_ url: URL, defaultValue: T, completion: @escaping (T) -> Void)

你正在尝试解码T.self

.decode(type: T.self, decoder: decoder)

应该是BitcoinPrice 类型才能工作。

请注意,来自您的链接的响应是一个单个对象,而不是一个数组。

但是,在您看来,您将 [BitcoinPrice] 作为默认值传递:

fetch(bitcoinPriceURL, defaultValue: [BitcoinPrice]() )  ...

推断T 的类型为[BitcoinPrice]

同时从BitcoinPrice 中删除var id = UUID()

struct BitcoinPrice: Decodable 
    let currency: String
    let rate: String

并像这样使用它:

List(exchangeRate, id: \.currency)  rate in

我还建议对集合使用 复数 名称,即。 exchangeRates 而不是 exchangeRate

【讨论】:

感谢您的帮助!与 T.self 我试图使代码通用,以便它可以与任何东西一起使用,你是说我应该用 BitcoinPrice 替换 T.self 吗?我试图将您的建议合并到我的代码中,但它说我缺少一些参数` @HenryHudson 你的代码是通用的没有问题。但是如果你想解码BitcoinPrice,你需要让你的泛型函数正确推断T类型。您可以通过传递一些 BitcoinPrice 变量作为默认值来做到这一点。 我明白了,我现在添加了 BitcoinPrice() 的 defaultValue,但我仍然遇到一些错误,认为主要是它说我缺少来自 @State private var exchangeRates = BitcoinPrice 的参数(),它需要大括号中的 from 参数我试图将代码发布到此评论中,但受到 cmets 允许的字符数的限制 @HenryHudson exchangeRate 是一个数组。当您分配解码值时:exchangeRate = $0 将其替换为exchangeRate = [$0]

以上是关于从 SwiftUI 中的 URL 获取 JSON的主要内容,如果未能解决你的问题,请参考以下文章

无法使用结合 SwiftUI 从 URL 获取响应

如何从 SwiftUI 中的 Bundle 中的有效路径或 URL 将图像加载到 Image()?

如何在 SwiftUI 中显示来自 url 的图像

如何使用 LinkPresentation 在 SwiftUI 中获取链接元数据?

SwiftUI 从 URL 加载单个对象 JSON

SwiftUI:从列表中的项目获取切换状态