Fetch JSON Data not working

Hello
I don't get any error when I fetch data but when I run I don't see any data
Thank you


Code Block
import SwiftUI
import Combine
struct ContentView: View {
@ObservedObject var networkController = NetworkController()
@State var search: String = ""
var body: some View {
NavigationView{
Form{
Section(){
TextField("Search", text: $search)
}
Section(){
List(networkController.users.filter {
$0.state.contains(search) }
, id: \.state){ user in
Text(user.state)
}
}
}
.navigationTitle("API")
}
}
}
class NetworkController: ObservableObject {
private var can: AnyCancellable?
let url = URL(string: "https://api.covidtracking.com/v1/states/current.json")!
@Published var users = [User(state: "")]
init() {
self.can = URLSession.shared.dataTaskPublisher(for: url)
.map { $0.data }
.decode(type: [User].self, decoder: JSONDecoder())
.replaceError(with: [])
.eraseToAnyPublisher()
.receive(on: DispatchQueue.main)
.sink(receiveValue: { users in
self.users = users
})
}
}
struct User: Decodable {
var state: String
}


Answered by OOPer in 654511022
@Jad-T
Thanks for showing the result. The most important part is the value of completion at the bottom:
Code Block
failure(Swift.DecodingError.valueNotFound(Swift.Int, Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 3", intValue: 3), CodingKeys(stringValue: "deathConfirmed", intValue: nil)], debugDescription: "Expected Int value but found null instead.", underlyingError: nil)))
  • "Expected Int value but found null instead." means some value, which you expect as integer, was not an integer but null.

  • _JSONKey(stringValue: "Index 3", intValue: 3) represents that was in the fourth element of the JSON array.

  • CodingKeys(stringValue: "deathConfirmed", intValue: nil) states that the key was "deathConfirmed".

In fact, when I access the URL directory with browser, I get this:
Code Block
[
...
{
"date": 20201227,
"state": "AS",
"positive": 0,
"probableCases": null,
"negative": 2140,
"pending": null,
"totalTestResultsSource": "totalTestsViral",
"totalTestResults": 2140,
"hospitalizedCurrently": null,
"hospitalizedCumulative": null,
"inIcuCurrently": null,
"inIcuCumulative": null,
"onVentilatorCurrently": null,
"onVentilatorCumulative": null,
"recovered": null,
"dataQualityGrade": "D",
"lastUpdateEt": "12/1/2020 00:00",
"dateModified": "2020-12-01T00:00:00Z",
"checkTimeEt": "11/30 19:00",
"death": 0,
"hospitalized": null,
"dateChecked": "2020-12-01T00:00:00Z",
"totalTestsViral": 2140,
"positiveTestsViral": null,
"negativeTestsViral": null,
"positiveCasesViral": 0,
"deathConfirmed": null,
"deathProbable": null,
"totalTestEncountersViral": null,
"totalTestsPeopleViral": null,
"totalTestsAntibody": null,
"positiveTestsAntibody": null,
"negativeTestsAntibody": null,
"totalTestsPeopleAntibody": null,
"positiveTestsPeopleAntibody": null,
"negativeTestsPeopleAntibody": null,
"totalTestsPeopleAntigen": null,
"positiveTestsPeopleAntigen": null,
"totalTestsAntigen": null,
"positiveTestsAntigen": null,
"fips": "60",
"positiveIncrease": 0,
"negativeIncrease": 0,
"total": 2140,
"totalTestResultsIncrease": 0,
"posNeg": 2140,
"deathIncrease": 0,
"hospitalizedIncrease": 0,
"hash": "2787ae39f1498a05878980d1f6195d5d78c19523",
"commercialScore": 0,
"negativeRegularScore": 0,
"negativeScore": 0,
"positiveScore": 0,
"score": 0,
"grade": ""
},
...
]


Can you see "deathConfirmed": null,? The API may return null for "deathConfirmed".

Your model type needs to be ready for null:
Code Block
struct User: Decodable, Hashable {
var state: String
var positive: Int
var deathConfirmed: Int? //<-
}


Your DetailView needs to adapt to this change:
Code Block
struct DetailView: View {
var gridItemLayout = [GridItem(.flexible()), GridItem(.flexible())]
var positive: Int
var state: String
var deathConfirmed: Int? //<-
var body: some View {
...
GroupBox{
VStack{
Text("State: ") //<-???
Divider()
Text(deathConfirmed.map{"\($0)"} ?? "?") //<-
.cornerRadius(16)
}
}.padding()
}
.navigationBarTitle(state)
}
}
}



Have you checked the content you get at each step of:
Code Block
self.can = URLSession.shared.dataTaskPublisher(for: url)
.map { $0.data }
.decode(type: [User].self, decoder: JSONDecoder())
.replaceError(with: [])
.eraseToAnyPublisher()
.receive(on: DispatchQueue.main)
.sink(receiveValue: { users in
self.users = users
})


Who you paste code, please use Paste and Match Style, to avoid all the extra blank lines.

Code Block
import SwiftUI
import Combine
struct ContentView: View {
@ObservedObject var networkController = NetworkController()
@State var search: String = ""
var body: some View {
NavigationView{
Form{
Section(){
TextField("Search", text: $search)
}
Section(){
List(networkController.users.filter {
$0.state.contains(search) }
, id: \.state){ user in
Text(user.state)
}
}
}
.navigationTitle("API")
}
}
}
class NetworkController: ObservableObject {
private var can: AnyCancellable?
let url = URL(string: "https://api.covidtracking.com/v1/states/current.json")!
@Published var users = [User(state: "")]
init() {
self.can = URLSession.shared.dataTaskPublisher(for: url)
.map { $0.data }
.decode(type: [User].self, decoder: JSONDecoder())
.replaceError(with: [])
.eraseToAnyPublisher()
.receive(on: DispatchQueue.main)
.sink(receiveValue: { users in
self.users = users
})
}
}
struct User: Decodable {
var state: String
}

Still not working (I changed some code)
Code Block
//
// Charts
//
// Created by Jad on 08/12/20.
//
import SwiftUI
import Combine
struct ContentView: View {
@ObservedObject var networkController = NetworkController()
@State var search: String = ""
var body: some View {
NavigationView{
Form{
Section(){
TextField("Search", text: $search)
}
Section(){
if search.isEmpty{
List(networkController.users, id: \.self){ user in
NavigationLink(destination: DetailView(positive: user.positive, deathConfirmed: user.deathConfirmed)){
Text(user.state)
}
}
} else{
List(networkController.users.filter{$0.state.contains(search)}, id: \.self){ user in
NavigationLink(destination: DetailView(positive: user.positive, deathConfirmed: user.deathConfirmed)){
Text(user.state)
}
}
}
}
}
.navigationTitle("API")
}
}
}
class NetworkController: ObservableObject {
private var can: AnyCancellable?
let url = URL(string: "https://api.covidtracking.com/v1/states/current.json")!
@Published var users = [User(state: "", positive: 0, deathConfirmed: 0)]
init() {
self.can = URLSession.shared.dataTaskPublisher(for: url)
.map { $0.data }
.decode(type: [User].self, decoder: JSONDecoder())
.replaceError(with: [])
.eraseToAnyPublisher()
.receive(on: DispatchQueue.main)
.sink(receiveValue: { users in
self.users = users
})
}
}
struct User: Decodable, Hashable {
var state: String
var positive: Int
var deathConfirmed: Int
}
//struct Records: Decodable, Hashable {
// var countriesAndTerritories: String
//}


I don't get any error

You do not get any error because you are replacing error with empty result.
In the line marked as <- in your code:
Code Block
init() {
self.can = URLSession.shared.dataTaskPublisher(for: url)
.map { $0.data }
.decode(type: [User].self, decoder: JSONDecoder())
.replaceError(with: []) //<-
.eraseToAnyPublisher()
.receive(on: DispatchQueue.main)
.sink(receiveValue: { users in
self.users = users
})
}


Please try changing the lines above as follows and tell us what you see:
Code Block
init() {
self.can = URLSession.shared.dataTaskPublisher(for: url)
.map { $0.data }
.decode(type: [User].self, decoder: JSONDecoder())
//.replaceError(with: [])
.eraseToAnyPublisher()
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: {completion in
print(completion)
}, receiveValue: { users in
self.users = users
})
}



I still don't get an error but I can't see any data

Code:
Code Block
//
// Charts
//
// Created by Jad on 08/12/20.
//
import SwiftUI
import Combine
struct ContentView: View {
@ObservedObject var networkController = NetworkController()
@State var search: String = ""
var body: some View {
NavigationView{
Form{
Section(){
TextField("Search", text: $search)
}
Section(){
if search.isEmpty{
List(networkController.users, id: \.self){ user in
NavigationLink(destination: DetailView(positive: user.positive, state: user.state, deathConfirmed: user.deathConfirmed)){
Text(user.state)
}
}
} else{
List(networkController.users.filter{$0.state.contains(search)}, id: \.self){ user in
NavigationLink(destination: DetailView(positive: user.positive, state: user.state, deathConfirmed: user.deathConfirmed)){
Text(user.state)
}
}
}
}
}
.navigationTitle("API")
}
}
}
class NetworkController: ObservableObject {
private var can: AnyCancellable?
let url = URL(string: "https://api.covidtracking.com/v1/states/current.json")!
@Published var users = [User(state: "", positive: 0, deathConfirmed: 0)]
init() {
self.can = URLSession.shared.dataTaskPublisher(for: url)
.map { $0.data }
.decode(type: [User].self, decoder: JSONDecoder())
//.replaceError(with: [])
.eraseToAnyPublisher()
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: {completion in
print(completion)
}, receiveValue: { users in
self.users = users
})
}
}
struct User: Decodable, Hashable {
var state: String
var positive: Int
var deathConfirmed: Int
}
struct DetailView: View {
var gridItemLayout = [GridItem(.flexible()), GridItem(.flexible())]
var positive: Int
var state: String
var deathConfirmed: Int
var body: some View {
ScrollView{
LazyVGrid(columns: gridItemLayout, spacing: 10){
GroupBox{
VStack{
Text("State: ")
Divider()
Text(String(state))
.cornerRadius(16)
}
}.padding()
GroupBox{
VStack{
Text("Positive: ")
Divider()
Text(String(positive))
.cornerRadius(16)
}
}.padding()
GroupBox{
VStack{
Text("State: ")
Divider()
Text(String(deathConfirmed))
.cornerRadius(16)
}
}.padding()
}
.navigationBarTitle(state)
}
}
}


```

I still don't get an error but I can't see any data

Have you activated the Debug Console?
From the main menu of Xcode, View > Debug Area > Activate Console.

Code Block
2020-12-28 14:36:28.125962+0100 Charts[47870:4787368] [] nw_protocol_get_quic_image_block_invoke dlopen libquic failed
2020-12-28 14:36:28.201001+0100 Charts[47870:4787265] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want. 
Try this: 
(1) look at each constraint and try to figure out which you don't expect; 
(2) find the code that added the unwanted constraint or constraints and fix it. 
(
    "<NSLayoutConstraint:0x600002f2fe30 'BIB_Trailing_CB_Leading' H:[_UIModernBarButton:0x7f86e8424430]-(6)-[_UIModernBarButton:0x7f86e8421fd0'API']   (active)>",
    "<NSLayoutConstraint:0x600002f2fe80 'CB_Trailing_Trailing' _UIModernBarButton:0x7f86e8421fd0'API'.trailing <= _UIButtonBarButton:0x7f86e8411020.trailing   (active)>",
    "<NSLayoutConstraint:0x600002f0cc80 'UINav_static_button_horiz_position' _UIModernBarButton:0x7f86e8424430.leading == UILayoutGuide:0x60000351f1e0'UIViewLayoutMarginsGuide'.leading   (active)>",
    "<NSLayoutConstraint:0x600002f0ccd0 'UINavItemContentGuide-leading' H:[_UIButtonBarButton:0x7f86e8411020]-(0)-[UILayoutGuide:0x60000351f100'UINavigationBarItemContentLayoutGuide']   (active)>",
    "<NSLayoutConstraint:0x600002f3d950 'UINavItemContentGuide-trailing' UILayoutGuide:0x60000351f100'UINavigationBarItemContentLayoutGuide'.trailing == _UINavigationBarContentView:0x7f86e8610bf0.trailing   (active)>",
    "<NSLayoutConstraint:0x600002f0d450 'UIView-Encapsulated-Layout-Width' _UINavigationBarContentView:0x7f86e8610bf0.width == 0   (active)>",
    "<NSLayoutConstraint:0x600002f3dd10 'UIView-leftMargin-guide-constraint' H:|-(0)-[UILayoutGuide:0x60000351f1e0'UIViewLayoutMarginsGuide'](LTR)   (active, names: '|':_UINavigationBarContentView:0x7f86e8610bf0 )>"
)
Will attempt to recover by breaking constraint 
Error:
<NSLayoutConstraint:0x600002f2fe30 'BIB_Trailing_CB_Leading' H:[_UIModernBarButton:0x7f86e8424430]-(6)-[_UIModernBarButton:0x7f86e8421fd0'API']   (active)>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.
failure(Swift.DecodingError.valueNotFound(Swift.Int, Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 3", intValue: 3), CodingKeys(stringValue: "deathConfirmed", intValue: nil)], debugDescription: "Expected Int value but found null instead.", underlyingError: nil)))

I tested in playground and got nothing.
Problem is probably your line 41.

I modified like this and seemed to work much better when appending after completion:

Code Block
var users : [User] = []
let can = URLSession.shared.dataTaskPublisher(for: uUrl)
.map { $0.data }
.decode(type: [User].self, decoder: JSONDecoder())
.replaceError(with: [])
.eraseToAnyPublisher()
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { print ("Received completion: \($0)."); print(users.count, users[1]) },
receiveValue: { user in print ("Received user: \(user).") ; users.append(contentsOf: user)
})

print gives:
56 User(state: "AL")

Note: see this https://developer.apple.com/documentation/foundation/urlsession/processing_url_session_data_task_results_with_combine
Still not working

Code Block
struct User: Decodable, Hashable {
var state: String
var positive: Int
var deathConfirmed: Int
}

I don't know why but when I delete deathConfirmed it works

Accepted Answer
@Jad-T
Thanks for showing the result. The most important part is the value of completion at the bottom:
Code Block
failure(Swift.DecodingError.valueNotFound(Swift.Int, Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 3", intValue: 3), CodingKeys(stringValue: "deathConfirmed", intValue: nil)], debugDescription: "Expected Int value but found null instead.", underlyingError: nil)))
  • "Expected Int value but found null instead." means some value, which you expect as integer, was not an integer but null.

  • _JSONKey(stringValue: "Index 3", intValue: 3) represents that was in the fourth element of the JSON array.

  • CodingKeys(stringValue: "deathConfirmed", intValue: nil) states that the key was "deathConfirmed".

In fact, when I access the URL directory with browser, I get this:
Code Block
[
...
{
"date": 20201227,
"state": "AS",
"positive": 0,
"probableCases": null,
"negative": 2140,
"pending": null,
"totalTestResultsSource": "totalTestsViral",
"totalTestResults": 2140,
"hospitalizedCurrently": null,
"hospitalizedCumulative": null,
"inIcuCurrently": null,
"inIcuCumulative": null,
"onVentilatorCurrently": null,
"onVentilatorCumulative": null,
"recovered": null,
"dataQualityGrade": "D",
"lastUpdateEt": "12/1/2020 00:00",
"dateModified": "2020-12-01T00:00:00Z",
"checkTimeEt": "11/30 19:00",
"death": 0,
"hospitalized": null,
"dateChecked": "2020-12-01T00:00:00Z",
"totalTestsViral": 2140,
"positiveTestsViral": null,
"negativeTestsViral": null,
"positiveCasesViral": 0,
"deathConfirmed": null,
"deathProbable": null,
"totalTestEncountersViral": null,
"totalTestsPeopleViral": null,
"totalTestsAntibody": null,
"positiveTestsAntibody": null,
"negativeTestsAntibody": null,
"totalTestsPeopleAntibody": null,
"positiveTestsPeopleAntibody": null,
"negativeTestsPeopleAntibody": null,
"totalTestsPeopleAntigen": null,
"positiveTestsPeopleAntigen": null,
"totalTestsAntigen": null,
"positiveTestsAntigen": null,
"fips": "60",
"positiveIncrease": 0,
"negativeIncrease": 0,
"total": 2140,
"totalTestResultsIncrease": 0,
"posNeg": 2140,
"deathIncrease": 0,
"hospitalizedIncrease": 0,
"hash": "2787ae39f1498a05878980d1f6195d5d78c19523",
"commercialScore": 0,
"negativeRegularScore": 0,
"negativeScore": 0,
"positiveScore": 0,
"score": 0,
"grade": ""
},
...
]


Can you see "deathConfirmed": null,? The API may return null for "deathConfirmed".

Your model type needs to be ready for null:
Code Block
struct User: Decodable, Hashable {
var state: String
var positive: Int
var deathConfirmed: Int? //<-
}


Your DetailView needs to adapt to this change:
Code Block
struct DetailView: View {
var gridItemLayout = [GridItem(.flexible()), GridItem(.flexible())]
var positive: Int
var state: String
var deathConfirmed: Int? //<-
var body: some View {
...
GroupBox{
VStack{
Text("State: ") //<-???
Divider()
Text(deathConfirmed.map{"\($0)"} ?? "?") //<-
.cornerRadius(16)
}
}.padding()
}
.navigationBarTitle(state)
}
}
}



So, your init would be

Code Block
init() {
var receivedUsers : [User] = []
self.can = URLSession.shared.dataTaskPublisher(for: url)
.map { $0.data }
.decode(type: [User].self, decoder: JSONDecoder())
.replaceError(with: [])
.eraseToAnyPublisher()
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { _ in users = receivedUsers },
receiveValue: { user in receivedUsers.append(contentsOf: user)
})
}


On your posts:
  • when you reply, please tell which proposal you replied to

  • don't just say "it doesn't work". Take time, as those who answer to help you, to explain what you exactly tried, what you get,

Thank you very much
It worked
Fetch JSON Data not working
 
 
Q