【问题标题】:Issues with JSONDecoder for API and Text a data in a View struct用于 API 的 JSONDecoder 和文本视图结构中的数据的问题
【发布时间】: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';只有结构/枚举/类类型可以符合协议并且类型“()”不能符合“视图”;只有结构/枚举/类类型可以符合协议

标签: swift swiftui


【解决方案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 中将其推送到我的视图
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多