Моя проблема: Я использую API сайта — https://www.themealdb.com/api.php.
Я хочу получить список всех продуктов. Для этого ссылка https://www.themealdb.com/api/json/v1/1/categories.php
В моем коде я создал структуру:
struct Category: Decodable {
var idCategory: Int?
var strCategory: String?
var strCategoryDescription: String?
var strCategoryThumb: String?
}
Затем я пытаюсь добраться до адреса и получить данные. Я могу преобразовать входящие данные в JSON. Оно работает.
Далее я хочу преобразовать данные и записать их в массив структур.
func load(url: String, completion: @escaping (_ objects: [Category])->()) {
guard let url = URL(string: url) else { return }
let session = URLSession.shared
session.dataTask(with: url) { (data, response, error) in
guard let data = data else { return }
do {
//let json = try? JSONSerialization.jsonObject(with: data, options: [])
//print("JSONSerialization" + "\(json)")
let object = try JSONDecoder().decode([Category].self, from: data)
print("JSONDecoder" + "\(object)")
completion(object)
} catch {
print(error.localizedDescription)
}
}.resume()
}
Но в этой строке получаю в консоли ошибку:
The data couldn’t be read because it isn’t in the correct format.
Вероятно, ошибка в моей структуре. Я не могу справиться с этой проблемой.
Я бы предложил использовать обработчик завершения [Category]?
или, лучше, Result<[Category], Error>
типа, чтобы вы могли отличить пустой набор результатов от ошибки. Это будет особенно полезно, если API имеет возможность фильтрации/поиска.
Вам нужно два кода
struct MyData: Codable {
var categories: [Category]?
}
А также
let object = try JSONDecoder().decode(MyData.self, from: data)
Есть ошибки два.
Фактическая ошибка
Type 'Array' mismatch: Expected to decode Array but found a dictionary instead.
указывает, что вы игнорируете корневой объект, словарь с ключом categories
Значение для ключа id
равно String
, а не Int
, обратите внимание на двойные кавычки в JSON.
Объявите все элементы структуры как константы необязательный, поскольку JSON предоставляет все ключи в словарях. И, пожалуйста, сопоставьте ужасные словарные ключи с более осмысленными именами участников.
И выведите все error
s и никогда.localizedDescription
в блоке Decodable
catch.
struct Response: Decodable {
let categories: [Category]
}
struct Category: Decodable {
let id: String
let name: String
let description: String
let thumbnailURL: URL
private enum CodingKeys: String, CodingKey {
case id = "idCategory"
case name = "strCategory"
case description = "strCategoryDescription"
case thumbnailURL = "strCategoryThumb"
}
}
func load(url: String, completion: @escaping ([Category]) -> Void) {
guard let url = URL(string: url) else { return }
let session = URLSession.shared
session.dataTask(with: url) { (data, _, error) in
if let error = error { print(error); return }
do {
let response = try JSONDecoder().decode(Response.self, from: data!)
print("JSONDecoder", response)
completion(response.categories)
} catch {
print(error)
completion([])
}
}.resume()
}
С помощью класса-оболочки вы можете получить свои категории. Следующий код отлично работает в Playground:
let json = """
{
"categories": [
{"idCategory": "1"},
{"idCategory": "2"}
]
}
"""
struct CategoryHolder: Codable {
var categories: [Category]
}
struct Category: Codable {
let idCategory: String?
let strCategory: String?
let strCategoryDescription: String?
let strCategoryThumb: String?
}
let jsonData = Data(json.utf8)
let categories = try JSONDecoder().decode(CategoryHolder.self, from: jsonData).categories
print(error.localizedDescription)
=>print(error)
. Это даст вам больше объяснений по ошибке.