Fetching from JSON SwiftUI

Hello
I'm trying to fetch some data from a JSON Api, but when I access the properties of my struct it tells me Value of Type "Name Of The Struct" has no member "name of the property"
Any idea on how to fix it
Code:

Code Block
import SwiftUI
import Combine
struct ContentView: View {
@ObservedObject var networkController = NetworkControllerItalia()
var gridItemLayout = [GridItem(.flexible()), GridItem(.flexible())]
var body: some View {
NavigationView{
ScrollView{
ForEach(networkController.users, id: \.self){ user in
GroupBox(label: Text(user.round ?? ""), content: {
VStack{
Text(user.team1 ?? "")
}
}).padding()
}
.navigationTitle("Serie A")
}
}
}
}
class NetworkControllerItalia: ObservableObject {
private var can: AnyCancellable?
let url = URL(string: "https://raw.githubusercontent.com/openfootball/football.json/master/2020-21/it.1.json")!
//let url = URL(string: "google.com")!
@Published var users = [UserItalia(matches: [])]
init() {
self.can = URLSession.shared.dataTaskPublisher(for: url)
.map { $0.data }
.decode(type: [UserItalia].self, decoder: JSONDecoder())
//.replaceError(with: [])
.eraseToAnyPublisher()
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: {completion in
print(completion)
}, receiveValue: { users in
self.users = users
})
}
}
struct UserItalia: Decodable, Hashable {
var matches: [Matches]?
}
struct Matches: Decodable, Hashable {
var round: String?
var date: String?
var team1: String?
var team2: String?
}

Thank You
Answered by Claude31 in 656540022

'ForEach' requires that 'UserItalia' conform to 'RandomAccessCollection'

Of course, UserItalia is not an array.
You need to understand better what your data structures are.

networkController.users is UserItalia type.

What you want to access are matches, isn't it ?

So, you should write something like this
Code Block
ForEach(networkController.users.matches ?? [], id: \.self){ match in
GroupBox(label: Text(match.round ?? ""), content: {
VStack{
Text(match.team1 ?? "")
}
}).padding()


I tested and it works (iOS 14.2).

If that's OK, don't forget to close the thread on this answer, otherwise please detail the exact error you get.

but when I access the properties of my struct it tells me Value of Type "Name Of The Struct" has no member "name of the property"

Do you mean "Matches has no member someName" ?

Could you show the JSON you get to check the coding keys ?
Why don't you define CodingKeys ?
I never used CodingKeys (I don't know what it's used for), but if it solves the problem I could include it
The JSON file is in the URL at line 28

It says Value of type UserItalia has no member team1.
OK, here is an extract from json
You could have made the effort to get it and post it yourself, isn't it ?

Code Block
{
"name": "Serie A 2020/21",
"matches": [
{
"round": "1^ Giornata",
"date": "2020-09-19",
"team1": "ACF Fiorentina",
"team2": "Torino FC",
"score": {
"ft": [
1,
0
]
}
},
{
"round": "1^ Giornata",
"date": "2020-09-19",
"team1": "Hellas Verona",
"team2": "AS Roma",
"score": {
"ft": [
3,
0
]
},
"status": "AWARDED"
},
{
"round": "1^ Giornata",
"date": "2020-09-20",
"team1": "Parma",
"team2": "SSC Napoli",
"score": {
"ft": [
0,
2
]
}
},


The error is on lines 30 and 35 where you pass an array.

Just pass:
Code Block
@Published var users = UserItalia(matches: [])

and
Code Block
.decode(type: UserItalia.self, decoder: JSONDecoder())


You could also decode the complete JSON:
Code Block
struct Score: Decodable, Hashable {
var ft: [Int]?
}
struct Matches: Decodable, Hashable {
var round: String?
var date: String?
var team1: String?
var team2: String?
var score: Score?
var status: String?
}
struct UserItalia: Decodable, Hashable {
var name: String?
var matches: [Matches]?
}


yes but now it gave me an error at line 13: Referencing initializer 'init(_:content:)' on 'ForEach' requires that 'UserItalia' conform to 'RandomAccessCollection'
I tried to change \.self with name but it didn't work

Accepted Answer

'ForEach' requires that 'UserItalia' conform to 'RandomAccessCollection'

Of course, UserItalia is not an array.
You need to understand better what your data structures are.

networkController.users is UserItalia type.

What you want to access are matches, isn't it ?

So, you should write something like this
Code Block
ForEach(networkController.users.matches ?? [], id: \.self){ match in
GroupBox(label: Text(match.round ?? ""), content: {
VStack{
Text(match.team1 ?? "")
}
}).padding()


I tested and it works (iOS 14.2).

If that's OK, don't forget to close the thread on this answer, otherwise please detail the exact error you get.

You need to understand better what your data structures are.

Maybe it's because I started programming/developing few weeks ago.
It worked, Thank you very much


I have the last question, is it possible (in the same ForEach) to display score.ft ?
If it works, thanks to mark this answer as correct.

From there you can also print the score:
Code Block
ForEach(networkController.users.matches ?? [], id: \.self){ match in
GroupBox(label: Text(match.round ?? ""), content: {
VStack {
HStack {
Text(match.team1 ?? "")
Text(" - ")
Text(match.team2 ?? "")
}
HStack { // THIS HStack is not needed, unless you want to add some other text in line.
Text(match.score == nil ? "" : "\(match.score!.ft![0]) - \(match.score!.ft![1])")
}
}
}).padding()

Thank you for your time, every thing is working
Fetching from JSON SwiftUI
 
 
Q