为啥我的 SwiftUI JSONDecoder 会出现致命错误?

Posted

技术标签:

【中文标题】为啥我的 SwiftUI JSONDecoder 会出现致命错误?【英文标题】:Why does my SwiftUI JSONDecoder give a fatal error?为什么我的 SwiftUI JSONDecoder 会出现致命错误? 【发布时间】:2021-04-25 18:32:43 【问题描述】:

我正在开发一个 SwiftUI 应用程序,该应用程序应该从 JSON 文件加载问题并将它们一一展示给用户。我关注了a tutorial 如何将JSON-data 加载到视图中,但它不起作用。 JSONDecoder 似乎失败了,有人知道为什么吗?

这是我的代码:

CodableBundleExtension.swift

import Foundation

extension Bundle 
    func decode(_ file: String) -> [Question] 
        // Locate the JSON-file.
        guard let url = self.url(forResource: file, withExtension: nil) else 
            fatalError("Failed to locate \(file) in bundle.")
        
        
        // Create a property for the data
        guard let data = try? Data(contentsOf: url) else 
            fatalError("Failed to load \(file) from bundle.")
        
        
        // Create a decoder
        let decoder = JSONDecoder()
        
        // Create a property for the decoded data
        guard let loaded = try? decoder.decode([Question].self, from: data) else 
            fatalError("Failed to decode \(file) from bundle.")
        
        
        // Return the decoded data
        return loaded
    

Questions.swift

import SwiftUI

struct Question: Codable, Identifiable 
    let id: Int
    let question: String
    let gamemode: Int
    let min_players: Int
    let max_platers: Int
    let question_type: Int
    let language: String
    

ContentView.swift

import SwiftUI

struct ContentView: View 
    
    var body: some View 
        let questions : [Question] = Bundle.main.decode("questions.json")
        
        ForEach(questions)  item in
            Text(item.question)
        
        ZStack 
            LinearGradient(
                gradient: Gradient(
                            colors: [.blue, .white]),
                            startPoint: .topLeading,
                            endPoint: .bottomTrailing)
                .edgesIgnoringSafeArea(.all)
            VStack 
                Text("The Questions App")
                    .font(.system(size: 36))
                    .fontWeight(.bold)
                    .foregroundColor(.white)
                    .padding(.top, 40)
                    .padding(.bottom, 40)
                VStack 
                    Image(systemName: "homepod")
                        .renderingMode(.original)
                        .resizable()
                        .frame(width: /*@START_MENU_TOKEN@*/100/*@END_MENU_TOKEN@*/, height: 140, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
                        .aspectRatio(contentMode: .fit)
                        
                    Spacer()
                    
                    Button 
                        print("tapped")
                     label: 
                        Text("Click Me")
                            .font(.system(size: 32))
                            .fontWeight(.medium)
                            .foregroundColor(.blue)
                            .frame(width: 200, height: 70, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
                            .background(Color.white)
                            .cornerRadius(20)
                            
                    
                    
                    Spacer()
                
                
            
        
    


struct ContentView_Previews: PreviewProvider 
    static var previews: some View 
        ContentView()
    

questions.json

[
    
        "id": 1,
        "question": "Testquestion 1",
        "gamemode": 1,
        "min_players": 2,
        "max_players": 4,
        "question_type": 1,
        "language": "NL"
    ,
    
        "id": 2,
        "question": "Testquestion 2",
        "gamemode": 1,
        "min_players": 2,
        "max_players": 99,
        "question_type": 2,
        "language": "NL"
    
]

这是错误:

Fatal error: Failed to decode questions.json from bundle.: file Drinking_App_V1/CodableBundleExtension.swift, line 27
2021-04-25 20:19:29.617268+0200 Questions App V1[13218:851770] Fatal error: Failed to decode questions.json from bundle.: file Questions_App_V1/CodableBundleExtension.swift, line 27
(lldb) 

【问题讨论】:

而不是 try? 使用 do-catch 并打印错误 @Andrew 我如何用这段代码做到这一点?我对 Swift 很陌生.. do let loaded = try decoder.decode(...) catch print(error) 见docs.swift.org/swift-book/LanguageGuide/ErrorHandling.html 第 32 行(已加载返回)现在给出错误:“无法将 'Bool' 类型的返回表达式转换为返回类型 '[Question]'”。我的代码是: do let loaded = try decoder.decode([Question].self, from: data) catch print(error) 【参考方案1】:

如果你使用 do/catch:

extension Bundle 
    func decode(_ file: String) -> [Question] 
        // Locate the JSON-file.
        guard let url = self.url(forResource: file, withExtension: nil) else 
            fatalError("Failed to locate \(file) in bundle.")
        
        
        do 
            let data = try Data(contentsOf: url)
            let decoder = JSONDecoder()
            return try decoder.decode([Question].self, from: data)
         catch 
            print(error)
            fatalError("Failed to decode \(file) from bundle.")
        
    

你会看到这个错误:

keyNotFound(CodingKeys(stringValue: "max_platers", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys(stringValue: \"max_platers\", intValue: nil) (\"max_platers\").", underlyingError: nil))

这告诉您,max_platers 在 JSON 中没有任何值,如您所见,这是 max_players 的拼写错误。

将您的模型转换为:

struct Question: Codable, Identifiable 
    let id: Int
    let question: String
    let gamemode: Int
    let min_players: Int
    let max_players: Int //<-- Here
    let question_type: Int
    let language: String

【讨论】:

以上是关于为啥我的 SwiftUI JSONDecoder 会出现致命错误?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 JsonDecoder 在尝试解析邮递员网址但处理其他网址时出现错误?

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

为啥我的 SwiftUI 列表没有填充我的所有数据?

为啥我在 SwiftUI 中的 TextEditor 会忽略我的 .keyboardType?

为啥我的 SwiftUI 列表分隔符没有正确排列?

为啥我的 SwiftUI 组件有一个圆圈反斜杠?