I am trying to fetch the Covid 19 data of all countries and their states from disease.sh .
I have previously fetched json data from different APIs using this method. The response in those cases were shorter compared to this.
I have posted the codes below:
// Webservice.swift
import Foundation
class Webservice {
let countriesURL: String = "https://disease.sh/v3/covid-19/jhucsse"
func getAllCountries(completion: @escaping ([Country]?) ->()) {
guard let url = URL(string: countriesURL) else {
completion(nil)
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print("No data in response: \(error?.localizedDescription ?? "Unknown error").")
DispatchQueue.main.async {
completion(nil)
}
return
}
let countries = try? JSONDecoder().decode([Country].self, from: data)
DispatchQueue.main.async {
countries == nil ? completion(nil) : completion(countries)
}
}.resume()
}
}
Since, I was using MVVM design, here are my Model, ViewModel and View.
// Model
// Country.swift
import Foundation
struct Country: Decodable {
var country: String
var updatedAt: String
var stats: Stats
var coordinates: Coordinates
var province: String
}
struct Stats: Decodable {
var confirmed: Int
var deaths: Int
var recovered: Int
}
struct Coordinates: Decodable {
var latitude: String
var longitude: String
}
// ViewModel
// CountryListViewModel.swift
import Foundation
class CountryListViewModel: ObservableObject {
@Published var countries = [CountryViewModel]()
init() {
fetchCountries()
}
func fetchCountries() {
Webservice().getAllCountries() { countries in
if let countries = countries {
self.countries = countries.map(CountryViewModel.init)
}
}
}
}
class CountryViewModel {
var country: Country
init(country: Country) {
self.country = country
}
let id = UUID()
var name: String {
return self.country.country
}
var updatedAt: String {
return self.country.updatedAt
}
var stats: Stats {
return self.country.stats
}
var coordinates: Coordinates {
return self.country.coordinates
}
var province: String {
return self.country.province
}
}
// View
// ContentView.swift
import SwiftUI
struct ContentView: View {
@ObservedObject private var countryListVM = CountryListViewModel()
var body: some View {
List( self.countryListVM.countries, id:\.id) { country in
Text(country.name)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
My issue is when I call the Webservice.getAllCountries() it returns nil. Could anyone look at the code and tell me what is wrong, please? Thank you!
PS: I created a mock json with fewer objects (20-30) and called Webservice.getAllCountries() in this case it returned and mapped the values. It is not working with larger JSON response. Help!!
You should avoid using try?
except in situations where you really don't care about failures. do/try/catch
is a better approach since it will tell you why something failed.
Changing your code to
do {
let countries = try JSONDecoder().decode([Country].self, from: data)
DispatchQueue.main.async {
completion(countries)
}
} catch {
print(error)
completion(nil)
}
Gives us an error on the console -
Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 509", intValue: 509), CodingKeys(stringValue: "province", intValue: nil)], debugDescription: "Expected String value but found null instead.", underlyingError: nil))
which makes sense, since not all countries have provinces. To fix this, make province
an optional in your data model
struct Country: Decodable {
var country: String
var updatedAt: String
var stats: Stats
var coordinates: Coordinates
var province: String?
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.