【问题标题】:Unable to read JSON from rest API无法从 REST API 读取 JSON
【发布时间】:2021-06-24 23:44:05
【问题描述】:

尝试读取此 JSON 数据:

{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
}

但我的应用程序不断崩溃,我很难理解原因(对于 swift 来说还是相当新的)。我认为数据存储在字典中,我只是处理不正确。有人可以解释一下解码这个 JSON 的正确方法以及我将如何在视图上显示它吗?我尝试用 JSONSerialization 代替 JSONDecoder,但得到了相同的结果,所以不确定这是否是正确的方向。

型号:

struct Model: Codable{
        var userId: Int? = nil
        var id: Int? = nil
        var title: String? = nil
        var completed: Bool? = nil
        
        enum CodingKeys: CodingKey{
           case userId, id, title, completed
        }
}

JSON 加载函数:

func loadData(){
        let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1")!
        
        //create url request
        var request = URLRequest(url: url)
        
        //specify the method to use
        request.httpMethod = "GET"
        
        //set HTTP request Header, can set more than one.
        request.setValue("application/json", forHTTPHeaderField: "Accept")

        //send the request
        
        let dataTask = URLSession.shared.dataTask(with: request){ data, response, error in
            
            if let data = data{
                if let users = try? JSONDecoder().decode(Model.self, from: data){
                   
                    DispatchQueue.main.async {
                        self.dataInfo = users
                    }
                }else{
                    print("Data Not Got")
                }
                
                
            }
            
            if let response = response{
                print("Response Got")
            }
            
            if let error = error{
                print("\(error)")
            }
        }
        dataTask.resume()
    }

Swift UI 视图:

struct ContentView: View {
    @State var dataInfo = Model()
    
    var body: some View {
        VStack{
            Button("Go"){
                loadData()
            }
            Text(dataInfo.title!)
}

【问题讨论】:

  • 您不应执行以下操作。 DispatchQueue.main.async {self.dataInfo = users}
  • 哦,谢谢你告诉我。你会建议做什么更好?

标签: json swift swiftui


【解决方案1】:

通过在title 之后使用!,您正在执行所谓的“强制展开”——告诉系统尽管变量/属性被声明为可选(在本例中为String?)你要保证它不是nil 并且那里 is 实际上是一个值。问题是,在您完成 API 调用之前,该属性 实际上是 nil,导致您的程序崩溃。

这是改变它的一种方法(解释如下):

struct Model: Codable{
    var userId: Int
    var id: Int
    var title: String
    var completed: Bool
}

struct ContentView: View {
    @State var dataInfo : Model?
    
    var body: some View {
        VStack{
            Button("Go"){
                loadData()
            }
            if let dataInfo = dataInfo {
                Text(dataInfo.title)
            }
        }
    }
    
    func loadData() {
     // your previous code here
    } 
}

在这个版本中,dataInfo 是一个 Optional,它在 API 调用时被设置。然后,if let dataInfo = dataInfo 执行一种称为“可选绑定”的操作,基本上是告诉系统仅在dataInfo 不是nil 的情况下运行以下代码。

最后,我已将您的 Model 更改为具有非可选属性,因为您使用的 API 调用会返回所有这些字段的值。如果您想保留以前的模型,您可能希望将我的代码更改为:

if let title = dataInfo?.title {
    Text(title)
}

查看 Swift 编程语言书籍以获取有关 Optional 以及如何使用它们的更多信息:https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html

【讨论】:

  • 非常感谢您的解释!我现在明白发生了什么好多了。
  • 没问题。虽然它与您的问题没有直接关系,但您可能还想考虑使用 dataTaskPublisher 和 Combine 来处理您的 API 请求。这是 SwiftUI 中的常见做法(尽管不是必需的):theswiftdev.com/urlsession-and-the-combine-framework
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-08-03
  • 2018-10-25
  • 2017-08-21
  • 2014-09-26
  • 1970-01-01
  • 2018-02-06
  • 1970-01-01
相关资源
最近更新 更多