No Value Associated with Key Codingkeys While Trying to Get Data from Github API in Xcode App

No value associated with key CodingKeys while trying to get data from GitHub API in Xcode app

First of all you don't need CodingKeys and the init method.

Second of all use structs, not classes.

If you want to decode the repositories you have to start with the root object, the repositories are in the array for key items

struct Root : Decodable {
let items : [Repository]
}

struct Repository: Decodable {
let id: Int
let name, fullName: String
let owner : Owner
}

struct Owner : Decodable {
let login : String
}

Another issue is that owner is also a Dictionary which becomes another struct.

To get rid of the CodingKeys add the .convertFromSnakeCase strategy which translates full_name into fullName.

let session = URLSession.shared
let url = URL(string: "https://api.github.com/search/repositories?q=CoreData&per_page=20")!
let task = session.dataTask(with: url) { data, response, error in
// Check the response
print(response)
// Check if an error occured
if let error = error {
// HERE you can manage the error
print(error)
return
}

// Serialize the data into an object
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let json = try decoder.decode(Root.self, from: data! )
print(json)
} catch {
print("Error during JSON serialization:", error)
}
}
task.resume()

Having trouble decoding JSON file from Github api swift 5

Your code works well for me, MoreInfo is decoded as required.
Using macos 12.2 Beta, Xcode 13.2,
targets ios 15 and macCatalyst 12. Tested on real devices.

You of course need to deal with the asynchronous function, as @Larme mentioned.
Try this approach:

func getMoreInfo(completion: @escaping(MoreInfo?) -> ()) {
if let url = URL(string: "https://api.github.com/repos/allegro/typescript-strict-plugin"){
URLSession.shared.dataTask(with: url) { data, responde, error in
if let data = data {
do {
let moreInfo = try JSONDecoder().decode(MoreInfo.self, from: data)
completion(moreInfo)
} catch let error {
print(error)
completion(nil) // todo deal with errors
}
}
}.resume()
}
}

and use it like this:

getMoreInfo() { info in
print("\n-------> info: \(info)")
}


OpenWeather One Call API coord not loading

OpenWeatherMap is well documented, please read the docs.

There are multiple different APIs, I guess your struct represents another API data.

The basic OneCall root object (omitting minutely, daily, hourly and alerts) is

struct OneCall: Decodable {
let lat, lon: Double
let timezone : String
let timezoneOffset : Int
let current: Current
}

And the descendants Current and Weather are

struct Current: Decodable {
let dt, sunrise, sunset : Date
let temp, feelsLike, dewPoint, uvi, windSpeed : Double
let pressure, humidity, clouds, visibility, windDeg : Int
let windGust : Double?
let weather : [Weather]
}

struct Weather: Decodable, Identifiable, CustomStringConvertible {
let id : Int
let main, description, icon : String
}

dt, sunrise and sunset are decoded as Date and the snake_case keys are converted to camelCase by applying appropriate decoding strategies.

I highly recommend to build the URL with URLComponents and URLQueryItems, apiKey is the API key constant.

let apiKey = "•••••••••"

enum WeatherManagerError : Error { case missingURL, badResponse }

class WeatherManager {
// HTTP request to get the current weather depending on the coordinates we got from LocationManager
func getCurrentWeather(latitude: CLLocationDegrees, longitude: CLLocationDegrees) async throws -> OneCall {

var urlComponents = URLComponents(string: "https://api.openweathermap.org/data/2.5/onecall")!
let queryItems = [URLQueryItem(name: "appid", value: apiKey),
URLQueryItem(name: "lat", value: "\(latitude)"),
URLQueryItem(name: "lon", value: "\(longitude)"),
URLQueryItem(name: "units", value: "metric")]
urlComponents.queryItems = queryItems
guard let url = urlComponents.url else { throw WeatherManagerError.missingURL }

let (data, response) = try await URLSession.shared.data(from: url)

guard (response as? HTTPURLResponse)?.statusCode == 200 else { throw WeatherManagerError.badResponse }
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
decoder.dateDecodingStrategy = .secondsSince1970
return try decoder.decode(OneCall.self, from: data)
}
}

Errors while trying to create a list based on JSON in SwiftUI

try something like this approach, to get you data from github. Works very well for me:

class NetworkingManager: ObservableObject{
@Published var gitHubList: [Item] = []

init() {
loadData()
}

func loadData() {
guard let url = URL(string: "https://api.github.com/search/repositories?q=CoreData&per_page=20") else { return }
URLSession.shared.dataTask(with: url) {(data, _, _) in
guard let data = data else { return }
do {
let response = try JSONDecoder().decode(Root.self, from: data)
DispatchQueue.main.async {
self.gitHubList = response.items
}
} catch {
print("error: \(error)")
}

}.resume()
}

}

struct Root: Codable {
let totalCount: Int
let incompleteResults: Bool
let items: [Item]

enum CodingKeys: String, CodingKey {
case totalCount = "total_count"
case incompleteResults = "incomplete_results"
case items
}
}

struct Item: Identifiable, Codable {
let keysURL, statusesURL, issuesURL: String
let id: Int
let url: String
let pullsURL: String
// ... more properties

enum CodingKeys: String, CodingKey {
case keysURL = "keys_url"
case statusesURL = "statuses_url"
case issuesURL = "issues_url"
case id
case url
case pullsURL = "pulls_url"
}
}

struct ContentView: View {
@StateObject var netManager = NetworkingManager()

var body: some View {
List {
ForEach(netManager.gitHubList) { item in
Text(item.url)
}
}
}
}

JSON decoding struct has a key, but Xcode calls that there is no member with that key in SwiftUI

netManager.repos is an array of Repository, it does not have a name, as the error tells you. You must select a single repo and get the name from that.

For example

Text("\(netManager.repos.first?.name ?? "no name")")

JSONDecoder fails to find key that is present

Try changing sshURL to sshUrl. The keyDecodingStartegy will transform sshURL into ssh_URL, which doesn't match your key. sshUrl will be transformed into ssh_url, which will match your key.

With JSONDecoder in Swift 4, can missing keys use a default value instead of having to be optional properties?

Approach that I prefer is using so called DTOs - data transfer object.
It is a struct, that conforms to Codable and represents the desired object.

struct MyClassDTO: Codable {
let items: [String]?
let otherVar: Int?
}

Then you simply init the object that you want to use in the app with that DTO.

 class MyClass {
let items: [String]
var otherVar = 3
init(_ dto: MyClassDTO) {
items = dto.items ?? [String]()
otherVar = dto.otherVar ?? 3
}

var dto: MyClassDTO {
return MyClassDTO(items: items, otherVar: otherVar)
}
}

This approach is also good since you can rename and change final object however you wish to.
It is clear and requires less code than manual decoding.
Moreover, with this approach you can separate networking layer from other app.

JSONDecoder fails to parse a list of maps


What's wrong with your code?

try?

That's the main culprit.
Why? You are ignoring the error thrown by the decode(_:from:). You are ignoring the error that could give you the exact reason or at least a hint on why it failed. Instead, write a proper do { try ... } catch { ... }.

So:

guard let data2 = data,
let tops = try? decoder.decode(TopResponse.self, from:
data2) else { return }
print(tops.results[4].given)

=>

guard let data2 = data else { return }
do {
let tops = try decoder.decode(TopResponse.self, from: data2)
print(tops.results[4].given)
} catch {
print("Got error while parsing: \(error)")
print("With response: \(String(data: data2, encoding: .utf8))") //Just in case because I've seen plenty of code where expected JSON wasn't the one received: it was an error, doc changed, etc...
}

Output for the first print:

$>Got error while parsing: keyNotFound(CodingKeys(stringValue: "results", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"results\", intValue: nil) (\"results\").", underlyingError: nil))

Fix:

struct TopResponse: Codable {
let results: [Top]

enum CodingKeys: String, CodingKey {
case results = "data"
}
}

Or rename results with data.

Then, next error:

$>Got error while parsing: typeMismatch(Swift.String, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "data", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "avg_score", intValue: nil)], debugDescription: "Expected to decode String but found a number instead.", underlyingError: nil))

Extract from JSON:

    "avg_score": 20.4

It's not a String (the value it's not between double quotes), that's a Double.

Fix:

let avg_score: String?

=>

let avg_score: Double?


Related Topics



Leave a reply



Submit