Error: keyNotFound(CodingKeys(stringValue: "origin", intValue: nil), - ios

**This question shows research effort; it is useful and clear
I am building a small swift dog fetch app using the DogsApi and I am running into some issues trying to parse the JSON. I have used the following function to parse the get and parse the JSON.
Below is my viewController:**
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var dogs = [DogStats]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
downloadJson {
print("Successful")
}
}
func downloadJson(completed: #escaping () -> ()) {
if let url = URL(string: "https://raw.githubusercontent.com/DevTides/DogsApi/master/dogs.json") {
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
do {
let dogs = try JSONDecoder().decode([DogStats].self, from: data)
print(dogs)
DispatchQueue.main.async {
self.dogs = dogs
self.tableView.reloadData()
}
} catch let error {
print(error)
}
}
}.resume()
}
}
}
below is my struct
import Foundation
struct DogStats: Codable {
let name: String
let origin: String
let breed_group: String!
let life_span: String
let temperament: String
}
Error in debugger
keyNotFound(CodingKeys(stringValue: "origin", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 5", intValue: 5)], debugDescription: "No value associated with key CodingKeys(stringValue: "origin", intValue: nil) ("origin").", underlyingError: nil))

origin, breed_group, temperament are not contained in all of your JSON objects in your API data. So there parse error occurred. You can make those optional(i.e. let origin: String?). You can also make all of your fields optional.
struct DogStats: Codable {
let name: String
let origin: String?
let breed_group: String?
let life_span: String
let temperament: String?
}

Related

Swift Dictionary API "No value associated with key CodingKeys(stringValue: \"type\", intValue: nil) (\"type\").", underlyingError: nil))

I am running into a error showing that there is no value for "type" from the API I'm trying to grab from. Ive tried looking through other posts revolving around this but can't find anything that works for me without causing a different issue to come up.
The full error I'm getting is "No value associated with key CodingKeys(stringValue: "type", intValue: nil) ("type").", underlyingError: nil))
The API I'm trying to grab from is: https://github.com/ToontownRewritten/api-doc/blob/master/invasions.md
import UIKit
struct InvasionList: Decodable {
let type: String
let asOf: Int?
let progress: String?
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let jsonUrlString = "https://www.toontownrewritten.com/api/invasions"
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 invasions = try JSONDecoder().decode(InvasionList.self, from: data)
print(invasions.type)
} catch let err {
print(err)
}
}.resume()
}
}
Your InvasionList doesn't actually match the structure of the JSON that you're trying to decode. It matches one small part of it, but you need to decode the entire thing. This includes the outermost wrapper (the part with lastUpdated and the dynamic keys for each "invasion").
Here's a working example:
let jsonData = """
{"lastUpdated":1624230138,"invasions":{"Gulp Gulch":{"asOf":1624230127,"type":"Robber Baron","progress":"3797/7340"},"Splashport":{"asOf":1624230129,"type":"Ambulance Chaser","progress":"2551/8000"},"Kaboom Cliffs":{"asOf":1624230131,"type":"Money Bags","progress":"5504/6000"},"Boingbury":{"asOf":1624230132,"type":"Tightwad","progress":"4741/8580"}},"error":null}
""".data(using: .utf8)!
struct InvasionWrapper: Decodable {
let lastUpdated : Int
let invasions : [String:Invasion]
}
struct Invasion: Decodable {
let type: String
let asOf: Int?
let progress: String?
}
do {
let list = try JSONDecoder().decode(InvasionWrapper.self, from: jsonData)
print(list)
} catch {
print(error)
}

Json Error typeMismatch(Swift.String, Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: [duplicate]

This question already has an answer here:
parsing Json in swift4
(1 answer)
Closed 1 year ago.
import UIKit
import Foundation
class ViewController: UIViewController{
#IBOutlet weak var tableView: UITableView!
var movies = [MoviesModel]()
let decoder = JSONDecoder()
override func viewDidLoad() {
super.viewDidLoad()
getJson {
print("successfully")
}
// Do any additional setup after loading the view.
}
func getJson(completed: #escaping () -> ()){
let url = URL(string: "https://api.androidhive.info/json/movies.json")
URLSession.shared.dataTask(with: url!) {
(data,response,error) in
if error == nil {
do{
self.movies = try
JSONDecoder().decode([MoviesModel].self,from:data!)
DispatchQueue.main.async {
completed()
}
}catch{
print("Json Error \(error)")
}
}
}.resume()
}
}
who can help me to solve this error
Json Error typeMismatch(Swift.String, Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "releaseYear", intValue: nil)], debugDescription: "Expected to decode String but found a number instead.", underlyingError: nil))
I would suggest spending more time understanding what's happening in the code that you posted, going through a brief overview:
let decoder = JSONDecoder()
let decodedMovies = decoder.decode([MoviesModel].self, from:data!)
Here you are trying to decode the data that is received from the request to movies request. Not entirely sure how your model looks, but it does seem like you want to define it like the following:
struct MoviesModel: Codable {
let title: String
let image: String
let rating: Double
let releaseYear: Int
let genre: [String]
}
Which should help to solve your decoding issues, as described in the error that you are posting.
Please also check Apple documentation on the subject - https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types

Swift and APIs: Decoding JSON tree

In my app, I have a song-searching feature where I call the Happi API (https://happi.dev/docs/music) and it will return songs with song info that matches a search keyword passed in.
Here is my code for accessing the data:
func getSongs(completion: #escaping(Result<[SongInfo], SongError>) -> Void) {
let dataTask = URLSession.shared.dataTask(with: resourceURL) { data, _, _ in
guard let jsonData = data else {
completion(.failure(.noDataAvailable))
return
}
do {
let decoder = JSONDecoder()
let songResponse = try decoder.decode(SongResponse.self, from: jsonData)
//print("decoded")
let songInfos = songResponse.response.results
completion(.success(songInfos))
}catch{
completion(.failure(.canNotProcessData))
}
}
dataTask.resume()
}
the let dataTask portion of the code is successful, and does not return the noDataAvailable error. However, I do receive a canNotProcessData error when I go decode the JSON data, and I assuming it's because of an error in my structure, but not entirely sure. Here's my structure:
struct SongResponse:Decodable {
var response:Results
}
struct Results:Decodable {
var results: [SongInfo]
}
struct SongInfo:Decodable {
var songName:String
var artist:String
}
Here are photos of my JSON tree from the API:image 1
image 2
Essentially, all I want is to retrieve each array in the results:[] dictionary, then for each array, I want to get the values for track and artist. For example in the image there are 5 results, and for each result i want to create a song class with "track" and "artist" attributes. These song classes would be stored in an array. Please let me know how to change my code, or of an easier way to do this!
EDIT: error message:
keyNotFound(CodingKeys(stringValue: "response", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: "response", intValue: nil) ("response").", underlyingError: nil))
Following is the correct structure
do {
let decoder = JSONDecoder()
let songResponse = try decoder.decode(SongResponse.self, from: jsonData)
let songInfos = songResponse.result ?? []
completion(.success(songInfos))
}catch{
completion(.failure(.canNotProcessData))
}
// Models
struct SongResponse: Decodable {
var success: Bool?
var result: [SongInfo]?
}
struct SongInfo: Decodable {
var track: String?
var artist: String?
}
To mirror the json structure, you have to change the key in SongResponse.
struct SongResponse: Decodable {
var result: [SongInfo]
}

Can't decode JSON because Content-Type missed

My JSONDecoder().decode can't decode data to json format, because server response has Content-Type like this "* \ *;charset=utf8".
What I have to do in this situation? Any ideas? API link
My code:
private static let livePhotoUrlString = "https://m1.kappboom.com/livewallpapers/info?o=0&v=575"
static func getLivePhotos(completionHandler: #escaping (([LivePhoto]) -> Void)) {
guard let livePhotoUrl = URL(string: livePhotoUrlString) else { return }
let semaphore = DispatchSemaphore(value: 0)
URLSession.shared.dataTask(with: livePhotoUrl) { (data, response, error) in
do {
guard let data = data else { return }
let livePhotos = try JSONDecoder().decode([LivePhoto].self, from: data)
completionHandler(livePhotos)
} catch {
completionHandler([])
}
semaphore.signal()
}.resume()
semaphore.wait()
}
My entity (LivePhoto):
class LivePhoto: Decodable {
init(smallUrl: String, largeUrl: String, movieUrl: String, id: Int, isLocked: Bool, promotionalUnlock: Bool) {
self.smallUrl = smallUrl
self.largeUrl = largeUrl
self.movieUrl = movieUrl
self.id = id
self.isLocked = isLocked
self.promotionalUnlock = promotionalUnlock
}
var smallUrl: String
var largeUrl: String
var movieUrl: String
var id: Int
var isLocked: Bool
var promotionalUnlock: Bool
}
Response headers:
Correct response (another API):
The error is not related to the content type.
Rather than ignoring the error in the catch block print it, decoding errors are very descriptive.
} catch {
print(error)
completionHandler([])
}
It states
keyNotFound(CodingKeys(stringValue: "smallUrl", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys(stringValue: \"smallUrl\", intValue: nil) (\"smallUrl\").", underlyingError: nil))
You can see immediately that the key is small_url and your struct member is smallUrl.
The easiest solution is to add the convertFromSnakeCase key decoding strategy
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let livePhotos = try decoder.decode([LivePhoto].self, from: data)
And you don't need the init method in the class. Declare it as struct with constant members
struct LivePhoto: Decodable {
let smallUrl, largeUrl, movieUrl: String
let id: Int
let isLocked: Bool
let promotionalUnlock: Bool
}
And please delete this horrible semaphore. As you are using a completion handler anyway it's pointless.
Are you sure that is the problem, to me it looks like you need to define coding keys for your struct
enum CodingKeys: String, CodingKey {
case smallUrl = "small_url"
case largeUrl = "large_url"
case movieUrl = "movie_url"
case isLocked = "is_locked"
case promotionalUnlock = "promotional_unlock"
case id
}
You need to use key names as they are in json , or write an enum with the converted names , but better to use convertFromSnakeCase
func getLivePhotos(completionHandler: #escaping (([LivePhoto]) -> Void)) {
guard let livePhotoUrl = URL(string: livePhotoUrlString) else { return }
URLSession.shared.dataTask(with: livePhotoUrl) { (data, response, error) in
print(data)
do {
guard let data = data else { return }
let dec = JSONDecoder()
dec.keyDecodingStrategy = .convertFromSnakeCase
let livePhotos = try dec.decode([LivePhoto].self, from: data)
completionHandler(livePhotos)
} catch {
print(error)
completionHandler([])
}
}.resume()
}
}
struct LivePhoto: Codable {
let id: Int
let smallUrl, largeUrl: String
let movieUrl: String
let isLocked, promotionalUnlock: Bool
}
Also it's a best practice to always print(error) inside the catch block , so you can know the error and fix , here there is no place for semaphores , it's only the job of the completion , also you may show an activity indicator until the request finishes as a better UX

unable to decode json with text in get request

I need to create GET request like this:
https://public-api.nazk.gov.ua/v1/declaration/?q=Чер
https://public-api.nazk.gov.ua/v1/declaration/?q=Володимирович
Last characters after = are Cyrillic symbols
I make get request something like this:
var hostURL = "https://public-api.nazk.gov.ua/v1/declaration/?q="
hostURL = hostURL + searchConditions
let escapedSearchConditions = hostURL.addingPercentEncoding(withAllowedCharacters:NSCharacterSet.urlQueryAllowed)
let url = URL(string: escapedSearchConditions!)!
Request is:
https://public-api.nazk.gov.ua/v1/declaration/?q=%D0%9F%D1%80%D0%BE
that return necessary data from server but returned data can't be decoded.
it works fine with integers in search condition but not with Cyrillic text(
import Foundation
struct Declarant: Codable {
var id: String
var firstname: String
var lastname: String
var placeOfWork: String
var position: String
var linkPDF: String
}
struct DeclarationInfo: Codable {
let items: [Declarant]
}
import Foundation
struct DeclarationInfoController {
func fetchDeclarationInfo (with searchConditions: String, completion: #escaping(DeclarationInfo?) -> Void) {
var hostURL = "https://public-api.nazk.gov.ua/v1/declaration/?q="
hostURL = hostURL + searchConditions
let escapedSearchConditions = hostURL.addingPercentEncoding(withAllowedCharacters:NSCharacterSet.urlQueryAllowed)
let url = URL(string: escapedSearchConditions!)!
print(url)
let dataTask = URLSession.shared.dataTask(with: url) {
(data, response, error) in
let jsonDecoder = JSONDecoder()
print("Trying to decode data...")
if let data = data,
let declarationInfo = try? jsonDecoder.decode(DeclarationInfo.self, from: data) {
completion(declarationInfo)
print(declarationInfo)
} else {
print("Either no data was returned, or data was not properly decoded.")
completion(nil)
}
}
dataTask.resume()
}
}
import UIKit
class DeclarationViewController: UIViewController {
let declarationInfoController = DeclarationInfoController()
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var resultLabel: UILabel!
#IBAction func beginSearchButton(_ sender: UIButton) {
declarationInfoController.fetchDeclarationInfo(with: searchBar.text!) { (declarationInfo) in
if let declarationInfo = declarationInfo {
DispatchQueue.main.async {
self.resultLabel.text = declarationInfo.items[0].lastname
}
}
}
}
}
Never ever use try? ignoring the error while decoding JSON. Codable errors are incredibly descriptive and tell you exactly what's wrong.
Use always a do catch block like
do {
let declarationInfo = try jsonDecoder.decode(DeclarationInfo.self, from: data)
} catch { print error }
and print the error rather than useless literal strings.
The error has nothing to do with Cyrillic text.
The suggested JSON struct in the comments of one of your previous questions
struct Item: Codable {
let id, firstname, lastname, placeOfWork: String
let position, linkPDF: String
}
reveals the error (the most significant parts are emphasized)
keyNotFound(CodingKeys(stringValue: "position", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "items", intValue: nil), _JSONKey(stringValue: "Index 11", intValue: 11)], debugDescription: "No value associated with key CodingKeys(stringValue: \"position\", intValue: nil) (\"position\").", underlyingError: nil))
It clearly describes that in the struct Item there is no value for key position in the item at index 11 of the array.
The solution is to declare this particular struct member as optional
struct Item: Codable {
let id, firstname, lastname, placeOfWork: String
let position : String?
let linkPDF: String
}
Once again: Don't ignore errors, they help you to fix the issues instantly.
Update
if let data = data,
let declarationInfo = try? jsonDecoder.decode(DeclarationInfo.self, from: data) {
completion(declarationInfo)
print(declarationInfo)
} else {
print("Either no data was returned, or data was not properly decoded.")
completion(nil)
}
by
do {
if let data = data {
let declarationInfo = try jsonDecoder.decode(DeclarationInfo.self, from: data)
completion(declarationInfo)
print(declarationInfo)
return
} catch {
print(error)
}
completion(nil)
You will have the error printed, ans do you will know why decoding fails

Resources