Background
As the title says, I'd like to make an expanding list view (like Paul does in the previous link) but with JSON instead of hardcoded data. In both his article and video on the subject, he says it's too messy with JSON - I'm not even sure if this is something that I should be pursuing or if it's just a waste of effort.
I'm using the iDine tutorial as a template. This is the source code (this will download the original iDine project to your computer - this link is found on the previous link's page about halfway down).
Basically, it is a resturant app, the menu is JSON. It is a nested JSON menu with 2 layers; contents of the first layer are the MenuSections (ex. breakfast) and the second layer are the MenuItems (ex. pancakes) - MenuSection
and MenuItem
are the structs inside the app to decode the JSON. I'd like to make all MenuItems (pancakes) collapse under MenuSection (breakfast).
Here is what the original structs look like:
import SwiftUI
struct MenuSection: Codable, Identifiable {
var id: UUID
var name: String
var items: [MenuItem]
}
struct MenuItem: Codable, Equatable, Identifiable {
var id: UUID
var name: String
var photoCredit: String
var price: Int
var restrictions: [String]
var description: String
var mainImage: String {
name.replacingOccurrences(of: " ", with: "-").lowercased()
}
var thumbnailImage: String {
"\(mainImage)-thumb"
}
}
Following Paul's example I've eliminated MenuSection
and added an items
variable to MenuItem
. The items
variable contains an optional array of MenuItem
- i.e. the same type as the struct (exactly what Paul does in his article). Example:
import SwiftUI
//struct MenuSection: Codable, Identifiable {
// var id: UUID
// var name: String
// var items: [MenuItem]
//}
struct MenuItem: Codable, Equatable, Identifiable {
var id: UUID
var name: String
var items: [MenuItem]? // <-----new variable
var photoCredit: String
var price: Int
var restrictions: [String]
var description: String
var mainImage: String {
name.replacingOccurrences(of: " ", with: "-").lowercased()
}
var thumbnailImage: String {
"\(mainImage)-thumb"
}
}
Problem
When I try to run the app, the build succeeds but the app then crashes because it cannot decode the JSON. It returns an error keyNotFound(CodingKeys(stringValue: "photoCredit", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys(stringValue: \"photoCredit\", intValue: nil) (\"photoCredit\").", underlyingError: nil))
.
If I'm interpreting this correctly, XCode is complaining that there is no value photoCredit in the top layer of my JSON (which used to be MenuSection but is now just another instance of MenuItem - JSON seen below).
Am I wrong to assume that if XCode doesn't find a matching value for a particular instance of a struct inside the associated JSON that it will just ignore it? I thought it would ignore it because in several other cases I've just had missing image files that would associate with var mainImage
or var thumbnailImage
if they were there but it never caused any issue - the images just never showed up.
Code
Here is a portion of the JSON menu for reference:
[
{
"id": "EF1CC5BB-4785-4D8E-AB98-5FA4E00B6A66",
"name": "Breakfast",
"items": [
{
"id": "EDCD038C-036F-4C40-826F-61C88CD84DDD",
"name": "Maple French Toast",
"photoCredit": "Joseph Gonzalez",
"price": 6,
"restrictions": ["G", "V"],
"description": "Sweet, fluffy, and served piping hot, our French toast is flown in fresh every day from Maple City, Canada, which is where all maple syrup in the world comes from. And if you believe that, we have some land to sell you…"
},
{
"id": "36A7CC40-18C1-48E5-BCD8-3B42D43BEAEE",
"name": "Stack-o-Pancakes",
"photoCredit": "Joseph Gonzalez",
"price": 8,
"restrictions": ["D", "G", "V"],
"description": "What do you get? Pancakes! How many do you get? One metric stack! That's equivalent to 1000 millistacks, or a tenth of a kilostack. In short, you get a lot of pancakes."
},
{
"id": "CFB8C2DB-3478-4A10-92FA-3D2A2D5324FB",
"name": "Power Muesli",
"photoCredit": "Hanny Naibaho",
"price": 5,
"restrictions": ["D", "N"],
"description": "Is normal muesli too boring? Of course it is. That's why we serve power muesli – it's like regular muesli, except we put the word \"power\" in front of it so we can charge more."
},
{
"id": "8903A204-F5C1-4D5C-A0A1-5C0ECD27FDD7",
"name": "Fresh-baked Croissant",
"photoCredit": "Kavita Joshi Rai",
"price": 3,
"restrictions": ["D", "G"],
"description": "This is just a regular croissant that we buy in bulk from a frozen foods supermarket, but we needed some sort of adjective to justify our price. So, it was either \"fresh-baked\" or \"authentic Parisian\", and our legal team thinks the latter is probably going to land us in hot water."
},
{
"id": "82B28AFF-BB29-4186-AA07-04959180D81A",
"name": "Full English",
"photoCredit": "Melissa Walker Horn",
"price": 12,
"restrictions": ["G"],
"description": "If you like eating breakfast out of things that could easily be served any time of day, then we have the perfect thing for you. Comes with tomato ketchup or brown sauce depending on how northern you sound."
},
{
"id": "190E1A9D-20A4-4F98-AA54-B192E1C4D7B9",
"name": "Porridge Deluxe",
"photoCredit": "Monika Grabkowska",
"price": 4,
"restrictions": ["D", "N", "V"],
"description": "This is literally just porridge. Bob, where did you get this picture? Our porridge looks nothing like this. No porridge does – who the heck has the time to make porridge like this? [NOTE FOR BOB: Replace this with actual marketing text]"
}
]
}
]
Any help is appreciated. Thanks!