用于 API 的 JSONDecoder 和文本视图结构中的数据的问题

Posted

技术标签:

【中文标题】用于 API 的 JSONDecoder 和文本视图结构中的数据的问题【英文标题】:Issues with JSONDecoder for API and Text a data in a View struct 【发布时间】:2021-03-30 13:30:45 【问题描述】:

我是 SwiftUI 的初学者,我正在尝试为 ios 开发一个应用程序。

我正在尝试使用 API 请求中的数据在我的主视图结构中对它们进行文本处理,我将不同的 vstack、hstack 等放在其中...

为此,我遵循了本教程:https://www.youtube.com/watch?v=lFE-TJJxxLU

我的 API 响应是:https://api.ethermine.org/miner/b5404f020334f52b33012af3587e69305eabee2c/dashboard

所以我这样准备我的模型:

import Foundation

struct ethermineResponse: Decodable 
    var status : String
    var data : dataDetails


struct dataDetails: Decodable 
    var statistics : [statistics]
    var workers : [workers]
    var currentStatistics : currentStatistics
    var settings : settings


struct statistics: Decodable 
    var time : Date
    var reportedHashrate : Double
    var currentHashrate : Double
    var validShares : Double
    var invalidShares : Int
    var staleShares : Int
    var activeWorkers : Int


struct workers: Decodable 
    var worker : String
    var time : Date
    var lastSeen : Date
    var reportedHashrate : Double
    var currentHashrate : Double
    var validShares : Double
    var invalidShares : Int
    var staleShares : Int


struct currentStatistics: Decodable 
    var time : Date
    var lastSeen : Date
    var reportedHashrate : Double
    var currentHashrate : Double
    var validShares : Double
    var invalidShares : Double
    var staleShares : Int
    var activeWorkers : Int
    var unpaid : Double


struct settings: Decodable 
    var monitor : Int
    var minPayout : Double
    var email : String

我编写了我的请求:

import Foundation

enum dataError: Error
    case noDataAvailable
    case canNotProcessData


struct ethermineRequest 
    let resourceURL:URL
    
    init(wallet: String) 
        let resourceString = "https://api.ethermine.org/miner/\(wallet)/dashboard"
        guard let resourceURL = URL(string: resourceString) else fatalError()
        
        self.resourceURL = resourceURL
    
    
    func getData(completionHandler: @escaping (Result<ethermineResponse, dataError>) -> Void)
        let task = URLSession.shared.dataTask(with: resourceURL)  data, _, _ in
            guard let jsondata = data else 
                completionHandler(.failure(.noDataAvailable))
                return
            
            do
                let ETHData = try JSONDecoder().decode(ethermineResponse.self, from: jsondata)
                
                completionHandler(.success(ETHData))
            catch
                print("error")
                
            

                    
        task.resume()
    

在我的主视图结构中,我试图在 VStack 中以文本为例,例如我的“未付”值:

VStack
                    let API = ethermineRequest(wallet: wallet)
                    API.getData  (ethermineResponse) in
                        Text(currentStatistics.unpaid!)
                    
                    Spacer()
                

但我有这个错误:

var body: some View

“无法生成表达式诊断;请提交错误报告”

我如何发送我的 API 请求中的未付金额等文本?

谢谢:)

【问题讨论】:

不相关但print("error") => print("Error: \(error)"),打印error,它可能会提供信息!。 我试过了,现在我有更多细节:类型'currentStatistics.Type'不能符合'StringProtocol';只有结构/枚举/类类型可以符合协议并且类型“()”不能符合“视图”;只有结构/枚举/类类型可以符合协议 【参考方案1】:

大多数问题可能是因为您对对象的命名方式。

例如,

 var statistics : [statistics]
 var workers : [workers]

编译器如何区分你的变量和你的struct

这些更糟糕,因为您甚至没有数组来使它们不同。

var currentStatistics : currentStatistics
var settings : settings

请以大写字母开头的classstruct 命名。

var statistics : [Statistics]
var settings : Settings
var currentStatistics : CurrentStatistics

有些教程是以其他方式教授的,但请注意所有来自 Apple 的 classstructprotocol,它们都不是以小写字母开头的 varlet

注意StringIntDoubleFoundationContentView 它们都以大写字母开头。

https://swift.org/documentation/api-design-guidelines/#fundamentals

这里的这条线也可能是一个问题。有几个原因。

let API = ethermineRequest(wallet: wallet)
                API.getData  (ethermineResponse) in

1st - 您正在body 中创建变量/发出请求。那项工作应该在class ViewModel 中,body 只是为了显示事物和触发动作而不做任何工作。

body 可以随时被许多触发器重新加载,每次重新加载时您都会执行该请求。

第二个 - unpaid 是一个 Double,所以要显示它,你必须使用它。

Text(currentStatistics.unpaid!.description)

但是currentStatistics 变量应该在ViewModel 中,因为它正在显示,所以它没有被创建。

我将所有struct 切换为以大写字母开头。下面是一种方法,但有很多方法可以实现。看看cmets。

class EthermineViewModel: ObservableObject 
    @Published var wallet: String = ""
        didSet
            getResponse(wallet: wallet)
        
    
    @Published var response: EthermineResponse?
    
    private func getResponse(wallet: String) 
        let request = EthermineRequest(wallet: wallet)
        request.getData  (ethermineResponse) in
            switch ethermineResponse
            case .failure(let error):
                //This should somehow trigger an Alert to let the user know there has been an error
                print(error)
            case .success(let response):
                self.response = response
            
        
    

struct EthermineView: View 
    @StateObject var vm: EthermineViewModel = EthermineViewModel()
    let wallet: String = "b5404f020334f52b33012af3587e69305eabee2c"
    var body: some View 
        Text(vm.response?.data.currentStatistics.unpaid.description ?? "nil")
            .onAppear()
                vm.wallet = wallet
            
    


struct EthermineView_Previews: PreviewProvider 
    static var previews: some View 
        EthermineView()
    

struct EthermineRequest 
    let resourceURL:URL
    
    init(wallet: String) 
        let resourceString = "https://api.ethermine.org/miner/\(wallet)/dashboard"
        guard let resourceURL = URL(string: resourceString) else 
            fatalError("resourceString == invalid url")
            
        
        
        self.resourceURL = resourceURL
    
    
    func getData(completionHandler: @escaping (Result<EthermineResponse, DataError>) -> Void)
        let task = URLSession.shared.dataTask(with: resourceURL)  data, urlResponse, responseError in
            guard let jsondata = data else 
                print(responseError ?? "responseError")
                //Is the absence of data the only type of error you could have?
                completionHandler(.failure(.noDataAvailable))
                return
            
            do
                let data = try JSONDecoder().decode(EthermineResponse.self, from: jsondata)
                
                completionHandler(.success(data))
            catch
                //this message will be much more useful
                print(error)
                //or
                let nsError = error as NSError
                print(nsError.localizedFailureReason ?? "no failure reason")
                print(nsError.localizedRecoveryOptions ?? "no recovery options")
                
            
            
        
        task.resume()
    
 

【讨论】:

你说的一切看起来都是真的,但是 OP 还试图在他的视图层次结构中间进行异步网络调用,这永远不会起作用。 谢谢我用你所说的更正了我的工作表,我的“var body”仍然出现错误“无法生成表达式诊断;请提交错误报告”。但现在更干净了:) 我同意我按照您的评论添加了这一点。 @DomTorreto 看看我添加的其余部分。将请求从View 中取出并放入class ViewModel : ObservableObject 中,只显示来自@Published var currentStatistics: CurrentStatistics 的数据 谢谢,但我不知道该怎么做API 并在不同的 vstack 和 hstack 中将其推送到我的视图

以上是关于用于 API 的 JSONDecoder 和文本视图结构中的数据的问题的主要内容,如果未能解决你的问题,请参考以下文章

如何准备 API 响应以在 swift 中与 jsonDecoder 一起使用

Python 3 中的 JSONdecoder 错误。来自 API 的 Json

如何使用 JSONDecoder 根据订单键获取排序数据?

JSONDecoder 不解析数据

Swift 4:JSONDecoder 在一种特定情况下失败-“操作无法完成”[关闭]

如何以这种日期格式使用 JSONDecoder / Codable?