Я хочу декодировать strIngredient1, strIngredient2, strIngredient3 в массив, а strMeasure1, strMeasure2, strMeasure3 в отдельный массив. Как мы можем это сделать, используя декодируемые и кодирующие ключи. У меня есть объект, соответствующий декодируемому протоколу, и функция public init (из декодера: Decoder).
{
"meals": [
{
"idMeal": "53049",
"strMeal": "Apam balik",
"strDrinkAlternate": null,
"strCategory": "Dessert",
"strArea": "Malaysian",
"strInstructions": "Mix milk, oil and egg together. Sift flour, baking powder and salt into the mixture. Stir well until all ingredients are combined evenly.\r\n\r\nSpread some batter onto the pan. Spread a thin layer of batter to the side of the pan. Cover the pan for 30-60 seconds until small air bubbles appear.\r\n\r\nAdd butter, cream corn, crushed peanuts and sugar onto the pancake. Fold the pancake into half once the bottom surface is browned.\r\n\r\nCut into wedges and best eaten when it is warm.",
"strMealThumb": "https://www.themealdb.com/images/media/meals/adxcbq1619787919.jpg",
"strTags": null,
"strYoutube": "https://thewikihow.com/video_6R8ffRRJcrg",
"strIngredient1": "Milk",
"strIngredient2": "Oil",
"strIngredient3": "Eggs",
"strIngredient4": "Flour",
"strIngredient5": "Baking Powder",
"strIngredient6": "Salt",
"strIngredient7": "Unsalted Butter",
"strIngredient8": "Sugar",
"strIngredient9": "Peanut Butter",
"strIngredient10": "",
"strIngredient11": "",
"strIngredient12": "",
"strIngredient13": "",
"strIngredient14": "",
"strIngredient15": "",
"strIngredient16": "",
"strIngredient17": "",
"strIngredient18": "",
"strIngredient19": "",
"strIngredient20": "",
"strMeasure1": "200ml",
"strMeasure2": "60ml",
"strMeasure3": "2",
"strMeasure4": "1600g",
"strMeasure5": "3 tsp",
"strMeasure6": "1/2 tsp",
"strMeasure7": "25g",
"strMeasure8": "45g",
"strMeasure9": "3 tbs",
"strMeasure10": " ",
"strMeasure11": " ",
"strMeasure12": " ",
"strMeasure13": " ",
"strMeasure14": " ",
"strMeasure15": " ",
"strMeasure16": " ",
"strMeasure17": " ",
"strMeasure18": " ",
"strMeasure19": " ",
"strMeasure20": " ",
"strSource": "https://www.nyonyacooking.com/recipes/apam-balik~SJ5WuvsDf9WQ",
"strImageSource": null,
"strCreativeCommonsConfirmed": null,
"dateModified": null
}
]
}





Вот что бы сделал я, который немного отличается от мнения комментатора. Я бы начал с создания собственной реализации CodingKey для двух пронумерованных полей:
enum MealNumberedFieldKey {
case ingredients(Int)
case measurements(Int)
}
extension MealNumberedFieldKey: CodingKey {
var intValue: Int? { nil }
init?(intValue: Int) {
return nil
}
init?(stringValue: String) {
var string = stringValue
if string.hasPrefix("strIngredient") {
string.removeFirst(13)
guard let number = Int(string) else { return nil }
self = .ingredients(number)
} else if string.hasPrefix("strMeasure") {
string.removeFirst(10)
guard let number = Int(string) else { return nil }
self = .measurements(number)
} else {
return nil
}
}
var stringValue: String {
switch self {
case .ingredients(let number):
return "strIngredient\(number)"
case .measurements(let number):
return "strMeasure\(number)"
}
}
}
Затем я бы написал специальную функцию init(from decoder:), используя два контейнера декодирования: один с обычными ключами кодирования, а другой с пользовательскими ключами кодирования:
struct Meal: Decodable {
let idMeal: String
let strMeal: String
// ...
let ingredients: [String]
let measurements: [String]
enum CodingKeys: String, CodingKey {
case idMeal
case strMeal
}
init(from decoder: any Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.idMeal = try container.decode(String.self, forKey: .idMeal)
self.strMeal = try container.decode(String.self, forKey: .strMeal)
// ...
let numberedContainer = try decoder.container(keyedBy: MealNumberedFieldKey.self)
self.ingredients = try (1...20).map { number in
try numberedContainer.decode(String.self, forKey: .ingredients(number))
}
self.measurements = try (1...20).map { number in
try numberedContainer.decode(String.self, forKey: .measurements(number))
}
}
}