Swift Error- keyNotFound(CodingKeys(stringValue:, intValue: nil), Swift.DecodingError.Context, - ios

I am trying to read off Connecticut coronavirus data from this API JSON File- "https://data.ct.gov/resource/rf3k-f8fg.json" ( but I'm getting this error from excode that says keyNotFound(CodingKeys(stringValue: "covid19TestsReported", intValue: nil), Swift.DecodingError.Context
However this error, only comes up when I try to access the covid_19_tests_reported property of the file.
Here is my code, can someone please tell me what I'm doing wrong.
import UIKit
class StateViewController: UIViewController {
#IBOutlet weak var testRatioLbl: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
getData()
}
fileprivate func getData(){
let url = URL(string: "https://data.ct.gov/resource/rf3k-f8fg.json")!
URLSession.shared.dataTask(with: url){(data, response, error) in
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let users = try decoder.decode([Users].self, from: data!)
print(users)
}
catch {
print(error)
}
}.resume()
}
func didUpdatePrice(tests: String) {
DispatchQueue.main.async {
self.testRatioLbl.text = tests + " tests"
}
}
func didFailWithError(error: Error) {
print(error)
}
}
class Users: Decodable {
let covid19TestsReported: String
enum CodingKeys: String, CodingKey {
case covid19TestsReported = "covid19TestsReported"
}
required init(from decoder:Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
covid19TestsReported = try values.decode(String.self, forKey: .covid19TestsReported)
}
}

update you class.. your key value is "covid_19_tests_reported" not "covid19TestsReported"
because this field is nil in some cases so make it optional
struct Users: Decodable {
let covid19TestsReported: String?
private enum CodingKeys: String, CodingKey {
case covid19TestsReported = "covid_19_tests_reported"
}
}
Also update your getData
fileprivate func getData(){
let url = URL(string: "https://data.ct.gov/resource/rf3k-f8fg.json")!
URLSession.shared.dataTask(with: url){(data, response, error) in
do {
let decoder = JSONDecoder()
let users = try decoder.decode([Users].self, from: data!)
users.forEach { (user) in
if let cases = user.covid19TestsReported {
print(cases)
}
}
}
catch {
print(error)
}
}.resume()
}

Related

Confused with Codable

I'm trying to fetch movie data using The Movie Database API and display them on a table view, but my code isn't actually getting any data.
class MovieData: NSObject, Decodable {
var movies: [Movie]?
private enum CodingKeys: String, CodingKey {
case movies = "results"
}
}
I think it has something to do with my coding keys because the movie data I want to retrieve are within an array. Seen here. But I'm a little confused with how Codable works.
class Movie: NSObject, Decodable {
var title: String?
var year: String?
var rate: Double?
var posterImage: String?
var overview: String?
private enum MovieKeys: String, CodingKey {
case title
case overview
case releaseDate = "release_date"
case rate = "vote_average"
case posterImage = "poster_path"
}
required init(from decoder: Decoder) throws {
let movieContainer = try decoder.container(keyedBy: MovieKeys.self)
// Get movie info
title = try movieContainer.decode(String.self, forKey: .title)
overview = try movieContainer.decode(String.self, forKey: .overview)
posterImage = try movieContainer.decode(String.self, forKey: .posterImage)
rate = try movieContainer.decode(Double.self, forKey: .rate)
// releaseDate = try movieContainer.decode(String.self, forKey: .releaseDate)
}
}
This is what I'm using to make the api call, so when the user loads into the view the API will fetch movie data and display it on a cell.
override func viewDidLoad() {
super.viewDidLoad()
let requestURL = URL(string: "https://api.themoviedb.org/3/movie/popular?api_key=\(apiKey)&language=en-US&page=1")
if let requestURL = requestURL {
Task {
do {
let (data, response) = try await URLSession.shared.data(from: requestURL)
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
throw MovieListError.invalidServerResponse
}
let decoder = JSONDecoder()
let movie = try decoder.decode(Movie.self, from: data)
print(data)
print(movie)
}
catch {
print(error)
}
}
}
}
It seems your are trying to decode a wrong object. It should be MovieData.self not Movie.self. And then you can iterate over the array of Movie.
override func viewDidLoad() {
super.viewDidLoad()
let requestURL = URL(string: "https://api.themoviedb.org/3/movie/popular?api_key=\(apiKey)&language=en-US&page=1")
if let requestURL = requestURL {
Task {
do {
let (data, response) = try await URLSession.shared.data(from: requestURL)
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
throw MovieListError.invalidServerResponse
}
let decoder = JSONDecoder()
// change Movie.self to MovieData.self
let movies = try decoder.decode(MovieData.self, from: data)
print(data)
print(movies)
}
catch {
print(error)
}
}
}
}

Cannot find 'ModelA' in scope using generic types

I have a call api function and parameter using generic types.
And I alse craete codable data model.
Why the function parameter don't get my custom struct model and get the error Cannot find 'ModelA' in scope.
Is my T type error?
I don't know how to fix it.
Thanks.
struct ResponseHeader :Codable {
let returnCode : String?
let returnMsg : String?
}
struct ModelA :Codable {
let responseHeader : ResponseHeader?
let responseBody : ResponseBody?
struct ResponseBody: Codable {
let name : String?
let age : String?
let email: String?
}
}
enum APIRouter: String {
case apiA = "http://localhost:3000/ApiA"
case apiB = "http://localhost:3000/ApiB"
case apiC = "http://localhost:3000/ApiC"
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.callApi(apiRouter: .apiA, model: ModelA) //Error. Cannot find 'ModelA' in scope
}
func callApi<T: Codable>(apiRouter: APIRouter, model: T.Type) {
let urlString = URL(string: apiRouter.rawValue)
if let url = urlString {
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard error == nil else { return }
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
if let data = data {
do {
let response = try decoder.decode(model.self, from: data)
print(response)
} catch {
print(error)
}
} else {
print("Error")
}
}
task.resume()
}
}
}
Add self at the end.
This generic function takes as an argument an Instance Type of model so you have to pass ModelA.self.
self.callApi(apiRouter: .apiA, model: ModelA.self) //Here

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()
}
}

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