#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
У вас будут свои данные, но поскольку все это опционально, необходимо кое-что развернуть, прежде чем это будет иметь смысл.