For example, how do I display the descriptors for each record in a list, such as:
- NUTTY, FRUITY
- FATTY
- FRUITY
(Note that I made descriptors optional in my model because the array might be empty)
Here is my JSON file code from file named flavors.json:
[
{
"id": "U45773",
"flavorGroup": "CASHEW",
"name": "NATURAL CASHEW FLAVORING",
"isBeer": true,
"isSeltzer": true,
"isNatural": true,
"descriptors": ["NUTTY", "FRUITY"],
"keywords": ["aromatic", "fattt-buttery", "brown", "nutty", "roasted", "creamy"]
},
{
"id": "U63639",
"flavorGroup": "BLACK WALNUT",
"name": "NATURAL AND ARTIFICIAL WALNUT FLAVOR",
"isBeer": true,
"isSeltzer": false,
"isNatural": true,
"descriptors": ["FATTY"],
"keywords": ["sweet", "molasses", "woody", "slight dried fruit (amber ale)"]
},
{
"id": "562811",
"flavorGroup": "APRICOT",
"name": "NATURAL AND ARTIFICIAL APRICOT FLAVOR",
"isBeer": true,
"isSeltzer": false,
"isNatural": true,
"descriptors": ["FRUITY"],
"keywords": ["juicy", "skunky", "peach", "floral", "slight green (sierra nevada pale ale)"]
}
]
Here is my model code:
struct Flavor: Codable, Identifiable {
enum CodingKeys: CodingKey {
case id
case flavorGroup
case name
case isBeer
case isSeltzer
case isNatural
case descriptors
case keywords
}
let id, flavorGroup, name: String
let isBeer, isSeltzer, isNatural: Bool
let descriptors, keywords: [String]?
}
Here is my view model code:
class ReadData: ObservableObject {
@Published var flavors = [Flavor]()
init(){
loadData()
}
func loadData() {
guard let url = Bundle.main.url(forResource: "flavors", withExtension: "json")
else {
print("Json file not found")
return
}
let data = try? Data(contentsOf: url)
let flavors = try? JSONDecoder().decode([Flavor].self, from: data!)
self.flavors = flavors!
}
}
This is my best attempt at the view code:
struct DescriptorListView: View {
@ObservedObject var datas = ReadData()
var body: some View {
List(datas.flavors) { item in
ForEach(item.descriptors, id: \.self) { descriptor in
Text("- \(descriptor)")
}
}
}
}
It produces these compiler errors that I do not understand how to fix:
Value of optional type '[String]?' must be unwrapped to a value of type '[String]'
Coalesce using '??' to provide a default when the optional value contains 'nil'
Force-unwrap using '!' to abort execution if the optional value contains 'nil'
CodePudding user response:
You were very close with your attempt. The major issue is that descriptors
is an Optional
. That means that you have to somehow unwrap that optional value -- I've used if let
, which is a technique called "optional binding".
The other issue is that your current code would list each descriptor on a different line. I've joined the descriptors together using joined
instead and presented them on one line.
struct DescriptorListView: View {
@ObservedObject var datas = ReadData()
var body: some View {
List(datas.flavors) { item in
HStack {
if let descriptors = item.descriptors {
Text(descriptors.joined(separator: ", "))
} else {
Text("(no data)")
}
}
}
}
}