SwiftUI — Декодирование JSON возвращает пустой экран

#json #swift #api #swiftui #codable

Вопрос:

Я пытаюсь декодировать объект JSON из удаленного API, Xcode не поднимает никаких флагов, но экран остается пустым, я не смог точно определить, откуда исходит ошибка, но если мне придется, я думаю, я думаю, что это как-то связано с моим анализом. Что здесь происходит не так? все кажется на своем месте

Это мое представление о содержании.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
    }
  ]
}
 

Комментарии:

1. I think it's something to do with my parsing try? => Не используйте > try? , сделайте свойство do / try / catch , вы увидите…

2. Тогда ответ будет довольно прямым, если мы прочитаем сообщение об ошибке или проанализируем вашу кодируемую структуру по сравнению с JSON, который вы показываете: .decode([UserProducts].self, => > .decode(UserProducts.self,

3. @Larme это все еще пустой экран, ничего не отображается

4. Сначала проверьте self.results = response.first , действительно ли он вызван, это проверка вашего синтаксического анализа. Если да, то ваша проблема больше не в синтаксическом анализе, а в части визуализации…

5. Почему все ваши кодируемые свойства структуры являются необязательными? Не могли бы вы распечатать String(data: data, encoding: .utf8) , чтобы быть уверенным в том, какой JSON вы получаете, по сравнению с тем JSON, который вы вставили, который, я думаю, вы должны (но, возможно, не настоящий) получить…

Ответ №1:

На самом деле у вас здесь две проблемы.

1-й, вот откуда берутся ваши нильсы:

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

Если вы прокомментируете это или заставите его делать то, что вы на самом деле хотите, чтобы он делал, а не то, что он делает в настоящее время (сводя все к нулю)… это приведет нас к проблеме № 2

У вас будут свои данные, но поскольку все это опционально, необходимо кое-что развернуть, прежде чем это будет иметь смысл.