【问题标题】:SwiftUI - Decoding a JSON returns blank screenSwiftUI - 解码 JSON 返回空白屏幕
【发布时间】:2021-11-09 16:42:30
【问题描述】:

我正在尝试从远程 API 解码 JSON 对象,Xcode 没有引发任何标志,但屏幕仍然空白,我无法确定错误来自哪里,但如果我不得不采取我猜,我认为这与我的解析有关。 这里出了什么问题?一切似乎都在它的位置

这是我的 ContentView.swift

导入 SwiftUI

结构内容视图:查看{

@State var results = UserProducts(status: Bool(), userProducts: [UserProduct]())

var body: some View {
    ScrollView(.horizontal, showsIndicators: false) {
        HStack(spacing: nil) {
            ForEach(0..<results.userProducts!.count) {
                res in
                VStack(){Text(verbatim: String(format: String(),((results.userProducts![res].id ?? "NA"))))}
            }.onAppear(perform: loadShelf)
        }         
    }
    Spacer()
}).background(Color(red: 250 / 255, green: 248 / 255, blue: 244 / 255))
}

func loadShelf(){
    
    guard let apiBaseURL = URL(string: "...") else {
        print("Base URL is invalid.")
        return
    }
    
    let request = URLRequest(url: apiBaseURL)
    
    URLSession.shared.dataTask(with: request) { data, response, error in
        DispatchQueue.main.async {
            if let data = data {
                do{
                    let decoded = try JSONDecoder().decode(UserProducts.self, from: data)
                    self.results = decoded
                    print("decoded: \(decoded)")
                    //prints: UserProducts(status: nil, userProducts: nil, recommendedProducts: nil)
                }
                catch{
                    print("Fetching data failed: \(error)")
                }
            }
        }
        }.resume()
    }
}

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

这是我的结构:

import Foundation

// MARK: - UserProducts
struct UserProducts: Codable {
    let status: Bool?
    let userProducts: [UserProduct]?

    enum CodingKeys: String, CodingKey {
        case status
        case userProducts = "user_products"
    }
}

// MARK: - UserProduct
struct UserProduct: Codable {
    let id, userID, productID: Int?
    let routineTime, createdAt, updatedAt: String?
    let archived, volume: Int?
    let deletedAt, addedBy, weeklyRoutineOne, weeklyRoutineTwo: String?
    let product: Product?
    let user: User?
    let phaseOut, refill, empty, recommended: Int?

    enum CodingKeys: String, CodingKey {
        case id
        case userID = "user_id"
        case productID = "product_id"
        case routineTime = "routine_time"
        case createdAt = "created_at"
        case updatedAt = "updated_at"
        case archived, volume
        case deletedAt = "deleted_at"
        case addedBy = "added_by"
        case weeklyRoutineOne = "weekly_routine_one"
        case weeklyRoutineTwo = "weekly_routine_two"
        case product, user
        case phaseOut = "phase_out"
        case refill, empty, recommended
    }
}

// MARK: - Product
struct Product: Codable {
    let productName, productDescription, productIngredients: String?
    let productPrice, volume: Int?
    let image, interference, activeIngredients, howToUse: String?
    let brandID, productTypeID: Int?
    let brand, type: Brand?
    let rating: JSONNull?

    enum CodingKeys: String, CodingKey {
        case productName = "product_name"
        case productDescription = "product_description"
        case productIngredients = "product_ingredients"
        case productPrice = "product_price"
        case volume, image, interference
        case activeIngredients = "active_ingredients"
        case howToUse = "how_to_use"
        case brandID = "brand_id"
        case productTypeID = "product_type_id"
        case brand, type, rating
    }
}

// MARK: - Brand
struct Brand: Codable {
    let id: Int?
    let name, createdAt, updatedAt, commission: String?
    let category: Int?

    enum CodingKeys: String, CodingKey {
        case id, name
        case createdAt = "created_at"
        case updatedAt = "updated_at"
        case commission, category
    }
}

// MARK: - User
struct User: Codable {
    let name, email: String?
    let image1, deviceToken: JSONNull?
    let account, followup: Bool?

    enum CodingKeys: String, CodingKey {
        case name, email, image1
        case deviceToken = "device_token"
        case account, followup
    }
}

// MARK: - Encode/decode helpers

class JSONNull: Codable, Hashable {

    public static func == (lhs: JSONNull, rhs: JSONNull) -> Bool {
        return true
    }

    public var hashValue: Int {
        return 0
    }

    public func hash(into hasher: inout Hasher) {
        // No-op
    }

    public init() {}

    public required init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if !container.decodeNil() {
            throw DecodingError.typeMismatch(JSONNull.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for JSONNull"))
        }
    }

    public func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encodeNil()
    }
}

现在这是 JSON 模型以及它的外观:

{
  "status": true,
  "user_products": [
    {
      "id": 1,
      "user_id": 1,
      "product_id": 1,
      "routine_time": "",
      "created_at": "",
      "updated_at": "",
      "archived": 0,
      "volume": 1,
      "deleted_at": "",
      "added_by": "",
      "weekly_routine_one": "",
      "weekly_routine_two": "",
      "product": {
        "product_name": "",
        "product_description": "",
        "product_ingredients": "",
        "product_price": 1,
        "volume": 1,
        "image": "",
        "interference": "",
        "active_ingredients": "",
        "how_to_use": "",
        "brand_id": 1,
        "product_type_id": 1,
        "brand": {
          "id": 1,
          "name": "",
          "created_at": "",
          "updated_at": "",
          "commission": ""
        },
        "type": {
          "id": 1,
          "name": "",
          "created_at": "",
          "updated_at": "",
          "category": 1
        },
        "rating": null
      },
      "user": {
        "name": "",
        "email": "",
        "image1": null,
        "device_token": null,
        "account": false,
        "followup": false
      },
      "phase_out": 0,
      "refill": 0,
      "empty": 0,
      "recommended": 0
    }
  ]
}

【问题讨论】:

  • I think it's something to do with my parsing + try? => 不要使用try?,做一个属性do/try/catch,你会看到...
  • 那么,如果我们阅读错误消息,或者分析您的 Codable 结构与您显示的 JSON,答案非常简单:.decode([UserProducts].self, => .decode(UserProducts.self,...
  • @Larme 它仍然是一个空白屏幕,没有任何显示
  • 首先检查self.results = response.first是否真的被调用,那就是检查你的解析。如果是,那么您的问题不再在于您的解析,而是在渲染部分......
  • 为什么你所有的 Codable 结构属性都是可选的?您能否打印String(data: data, encoding: .utf8) 来确定您收到的 JSON 与您粘贴的 JSON,我猜您应该(但可能不是真正的)收到...

标签: json swift api swiftui codable


【解决方案1】:

这里实际上有两个问题。

第一,这是你的 nil 的来源:

    public func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
       try container.encodeNil()
    }

如果你把它注释掉或者让它做你真正想要它做的事情,而不是它当前正在做的事情(让一切都为零)......这会给我们带来问题#2

您将拥有自己的数据,但由于它们都是可选的,因此需要先进行一些解包,然后才会有意义。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-12-04
    • 2017-08-06
    • 2018-03-28
    • 1970-01-01
    • 1970-01-01
    • 2019-06-21
    • 1970-01-01
    • 2015-07-09
    相关资源
    最近更新 更多