简体   繁体   中英

How to make GET API calls that has large JSON response in Swift 5?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM