Decoding Huge JSON Array URL using swift - ios

I'm trying to decode data from this massive JSON Array https://coronavirus-19-api.herokuapp.com/countries I've had luck decoding by country or using the total stat worldwide https://coronavirus-19-api.herokuapp.com/all
by doing the following
//
// GlobalSwiftViewController.swift
// Universal
//
// Created by JOE on 3/20/20.
import UIKit
final class StatSwiftViewController: UIViewController {
// THESE LABELS WILL RETRIEVE THE FOLLOWING DATA FROM THE URL: THE CASE , DEATH AND RECOVERED DATA
#IBOutlet weak var CaseLable: UILabel!
#IBOutlet weak var DeathLable: UILabel!
#IBOutlet weak var RecoveredLabel: UILabel!
struct JSONTest: Decodable {
let cases: Double
let deaths: Float
let recovered: Int?
}
override func viewDidLoad() {
super.viewDidLoad()
let urlString = "https://coronavirus-19-api.herokuapp.com/all"
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!.localizedDescription)
}
guard let data = data else { return }
do {
//Decode data
let urlString = try JSONDecoder().decode(JSONTest.self, from: data)
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
//HERE WE ARE SHOWING TO THE USER THE DATA FROM THE URL ABOVE
DispatchQueue.main.async {
self.CaseLable.text = numberFormatter.string(for: urlString.cases)
self.DeathLable.text = numberFormatter.string(for: urlString.deaths)
self.RecoveredLabel.text = numberFormatter.string(for: urlString.recovered)
//self.timeLabel.text = JSONTest.deaths
}
} catch let jsonError {
print(jsonError)
}
}.resume()
}
}
Now I'm trying to decode all of the data in this URL https://coronavirus-19-api.herokuapp.com/countries to show in one view controller, I've had success by using the single URL https://coronavirus-19-api.herokuapp.com/countries/china for the country using the same code above by just adding more vars and labels However, I'm not able to add more counties by adding each URL for each country or using the main URL for all countries https://coronavirus-19-api.herokuapp.com/countries Therefore, How can I struct all Array List using the URL for all countries?
note: Im trying to edit/update my code above to get the results as possible without installing extra pods or files...

Try to adapt your model to be able to decode the countries data.
You can test this in a Playground:
import Foundation
struct JSONTestElement: Codable {
let country: String
let cases, todayCases, deaths, todayDeaths: Int
let recovered, active, critical, casesPerOneMillion: Int
}
typealias JSONTest = [JSONTestElement]
func decode() {
let urlString = "https://coronavirus-19-api.herokuapp.com/countries"
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!.localizedDescription)
}
guard let data = data else { return }
do {
//Decode data
let countriesData = try JSONDecoder().decode(JSONTest.self, from: data)
let china = countriesData.filter({ $0.country.contains("China")})
print("China data: \(china)")
} catch let jsonError {
print(jsonError)
}
}.resume()
}
decode()

Related

Nothing appearing the console when Decoding JSON in Swift

I'm trying to play around with a COVID dataset from Github (link in code below) but when I run the code nothing appears in the console. There are no errors appearing.
Can anyone advise on whats wrong here? Thanks in advance!
struct country: Decodable {
var location: String
var new_cases: Double
var people_fully_vaccinated: Double
}
func getJSON(){
guard let url = URL(string: "https://raw.githubusercontent.com/owid/covid-19-data/68c39808d445fe90b1fe3d57b93ad9be20f796d2/public/data/latest/owid-covid-latest.json") else{
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request){ (data, response, error) in
if let error = error{
print(error.localizedDescription)
return
}
guard let data = data else{
return
}
let decoder = JSONDecoder()
guard let decodedData = try? decoder.decode([country].self, from: data) else{
return
}
let countries = decodedData
for country in countries{
print (country.location)
}
}.resume()
}
getJSON()
You need
struct Root: Decodable {
var location: String
var new_cases: Double? // make it optional as it has some objects with nil
var people_fully_vaccinated: Double? // make it optional as it has some objects with nil
}
With
do {
let res = try decoder.decode([String:Root].self, from: data)
let locations = Array(res.values).map { $0.location }
print(locations)
}
catch {
print(error)
}

Got this error: nw_protocol_get_quic_image_block_invoke dlopen libquic failed

I was trying to connect my API data to view it in the cell but it seems that I can't get my response and it's always == nil
The code below describes the Country.SWIFT // Model.SWIFT // Response.SWIFT which is showing how can I get my JSON response using Codable
and CountryCell.SWIFT is showing how I used it to call the API
The link to the API image:
Country.SWIFT
struct Country: Decodable {
var CountryName = ""
var CountryImage = ""
var objectId = ""
// MARK: - Coding Keys
enum CodingKeys: String, CodingKey {
case CountryName = "CountryName"
case CountryImage = "CountryImage"
case objectId = "objectId"
}
//MARK: - Json Decoder
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// Parsing our attributes
self.CountryName = try container.decode(String.self, forKey: .CountryName)
self.CountryImage = try container.decode(String.self, forKey: .CountryImage)
self.objectId = try container.decode(String.self, forKey: .objectId)
}
}]
Model.SWIFT
protocol ModelDelegate {
func countriesFetched (_ countries: [Country])
}
class Model {
//MARK: - Vars
var delegate: ModelDelegate?
// MARK: - Get Countries
func getCountries () {
// URL Object
let url = URL(string: Constants.API_URL)
guard url != nil else {return}
// URL Session object
let session = URLSession.shared
//Data Task from URLSession object
let dataTask = session.dataTask(with: url!) { (data, response, error) in
if error != nil || data == nil {
print(error!.localizedDescription)
return
}
print(data!)
do {
let decoder = JSONDecoder()
let response = try decoder.decode(Response.self, from: data!)
if response.items != nil {
DispatchQueue.main.async {
self.delegate?.countriesFetched(response.items!)
}
}
}
catch {
}
}
// start data task
dataTask.resume()
}
}
Response.SWIFT
struct Response: Decodable {
var items: [Country]? = []
init(from decoder: Decoder) throws {
var itemsContrainer = try decoder.unkeyedContainer()
self.items = try itemsContrainer.decode([Country].self)
}
}
CountryCell.SWIFT
class CountryCell: UICollectionViewCell {
//MARK: - Vars
var country: Country?
//MARK: - Outlets
#IBOutlet weak var imageViewCountryOutlet: UIImageView!
#IBOutlet weak var lblCountryNameOutlet: UILabel!
//MARK: - Creating Cell
func generateCell (_ myCountry: Country) {
self.country = myCountry
guard self.country != nil else { return }
lblCountryNameOutlet.text = country!.CountryName
guard self.country!.CountryImage != "" else {return}
let url = URL(string: self.country!.CountryImage)
guard url != nil else {return}
let session = URLSession.shared
let dataTask = session.dataTask(with: url!) { (data, response, error) in
if error == nil || data != nil {
if url!.absoluteString != self.country!.CountryImage {
return
}
let image = UIImage(data: data!)
DispatchQueue.main.async {
self.imageViewCountryOutlet.image = image
}
}
}
dataTask.resume()
}
}
There is no need for the wrapping Response type, decode the list of countries directly:
let decoder = JSONDecoder()
let items = try decoder.decode([Country].self, from: data!)
In your code, when you ask in Response for unkeyedContainer, the expected JSON structure would need additional nested array.
Check decoding errors in the catch block with
do {
...
}
catch {
print(error)
}
I know it sounds bit absurd but in my case, by using class instead of struct and declare all attributes with public var worked for me.

JSON throws nil on every request

First time I'm trying to parse JSON data. The output comes out -
Course (success: zero, timestamp: nil, base: nil, date: nil, courses: nil)
Why is there nil everywhere?
I tried to change the value in the "Course" and "Currency" structures but did not lead to success
import UIKit
import JavaScriptCore
struct Course: Decodable {
var success: Bool?
var timestamp: Int?
var base: String?
var date: String?
var rates: Currency?
}
struct Currency: Decodable {
var USD: Float
var AUD: Double
var CAD: Double
var PLN: Double
var MXN: Double
}
class JsonViewContoller: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let urlData: String = "http://data.fixer.io/api/latest?access_key=7ac2982c82da926b787fd2f089b110e5&symbols=USD,AUD,CAD,PLN,MXN&format=1"
guard let url = URL(string: urlData) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { return }
guard error == nil else { return }
do {
let course = try JSONDecoder().decode(Course.self, from: data)
print(Course())
} catch let error {
print(error)
}
}.resume()
}
}
I have run your code you don't need to use two structures to parse the above JSON data. You can easily get Data in a single Structure. I have modified your code as follows:-
import UIKit
struct Course: Codable {
let success: Bool?
let timestamp: Int?
let base, date: String?
let rates: [String: Double]?
}
class JsonViewContoller: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
courseData()
}
fileprivate func courseData(){
let urlData: String = "http://data.fixer.io/api/latest?access_key=7ac2982c82da926b787fd2f089b110e5&symbols=USD,AUD,CAD,PLN,MXN&format=1"
guard let url = URL(string: urlData) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { return }
guard error == nil else { return }
do {
let course = try JSONDecoder().decode(Course.self, from: data)
print(course)
} catch let error {
print(error)
}
}.resume()
}
}

Why JsonDecoder Giving Error while trying to parse postman Url but working on other urls?

Both The Url contains Dictionary In Json Format.
import UIKit
import Foundation
struct Course : Decodable{
let foo1: String?
let foo2: String?
let fullName : String?
let numFound : Int?
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// let jsonUrl = "https://api.plos.org/search?q=title:DNA" //Working On this Url
let jsonUrl = "https://postman-echo.com/get?foo1=bar1&foo2=bar2"//Not Working On this Url
guard let url = URL(string: jsonUrl) else { return}
URLSession.shared.dataTask(with: url) {(data,response,err) in
guard let data = data
else {return}
do
{
let course = try JSONDecoder().decode([String: Course].self , from: data)
course.forEach { print("\($0.key): \($0.value)")}
}
catch let jerr
{
print(jerr)
}
}.resume()
}
}
This Is the Error If I am Using Postman Url
Paste the Postman URL into a browser and look at the JSON.
It doesn't work because there are other values than Course for other keys so decoding a [String:Course] dictionary fails.
You have to add an umbrella struct for the root object
struct Root : Decodable {
let args : Course
}
struct Course : Decodable {
let foo1: String?
let foo2: String?
let fullName : String?
let numFound : Int?
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// let jsonUrl = "https://api.plos.org/search?q=title:DNA" //Working On this Url
let jsonUrl = "https://postman-echo.com/get?foo1=bar1&foo2=bar2"//Not Working On this Url
guard let url = URL(string: jsonUrl) else { return}
URLSession.shared.dataTask(with: url) {(data,response,err) in
guard let data = data else {return}
do {
let result = try JSONDecoder().decode(Root.self , from: data)
print(result.args.foo1, result.args.foo2)
} catch {
print(error)
}
}.resume()
}
}

Display information from decoded JSON in a UILabel (Swift 4)

I am trying to display information taken from JSON. I've used .decode to get it. Now I want to put its text onto a simple label on my storyboard. At the bottom under ".resume()" is my attempt and it isn't working. I can't seem to figure this out.
import UIKit
struct WebsiteDescription: Decodable {
var name : String
var description : String
var courses : [Course]
}
struct Course: Decodable {
let id: Int
let name: String
let link: String
let imageUrl: String
}
class ViewController: UIViewController {
#IBOutlet weak var displayLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let jsonUrlString = "https://api.letsbuildthatapp.com/jsondecodable/website_description"
guard let url = URL(string: jsonUrlString) else {return}
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else {return}
do {
let websiteDescription = try JSONDecoder().decode(WebsiteDescription.self, from: data)
print(websiteDescription.name, websiteDescription.description, websiteDescription.courses)
//let courses = try JSONDecoder().decode([Course].self, from: data)
} catch let jsonErr {
print("Error serializing json", jsonErr)
}
}.resume()
let displayLabel.text = websiteDescription.name
}
}
You need to update your label from within the do method. Also you should do it from the main thread so that the UI can be updated. I have updated the code below.
import UIKit
struct WebsiteDescription: Decodable {
var name : String
var description : String
var courses : [Course]
}
struct Course: Decodable {
let id: Int
let name: String
let link: String
let imageUrl: String
}
class ViewController: UIViewController {
#IBOutlet weak var displayLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let jsonUrlString = "https://api.letsbuildthatapp.com/jsondecodable/website_description"
guard let url = URL(string: jsonUrlString) else {return}
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else {return}
do {
let websiteDescription = try JSONDecoder().decode(WebsiteDescription.self, from: data)
print(websiteDescription.name, websiteDescription.description, websiteDescription.courses)
//let courses = try JSONDecoder().decode([Course].self, from: data)
DispatchQueue.main.async {
self.displayLabel.text = websiteDescription.name
}
} catch let jsonErr {
print("Error serializing json", jsonErr)
}
}.resume()
}

Resources