Response Struct Does Not Like Codingkeys

Response struct does not like CodingKeys

From the documentation Encoding and Decoding Custom Types:

Codable types can declare a special nested enumeration named
CodingKeys that conforms to the CodingKey protocol. When this
enumeration is present, its cases serve as the authoritative list of
properties that must be included when instances of a codable type are
encoded or decoded. The names of the enumeration cases should match
the names you've given to the corresponding properties in your type.

In other words:

struct CodableStruct: Codable {
let myPropertyName: SomeCodableType

enum CodingKeys: String, CodingKey {
case myPropertyName = "WhateverIsMatchingInRealityTheJSON"
}
}

And it's mandatory that myPropertyName be the name of the var of CodableStruct AND the name of the case of the CodingKeys.

In your case, since it's recommended to name the var starting with a lowercase, I'll go that way:

struct AdminResponse: Codable {
let status: Int?
let superUsers: [SuperUser]?
let message: String?

enum CodingKeys: String, CodingKey {
case status = "Status"
case superUsers = "SuperUsers"
case message = "Message"
}
}

Swift Error: keyNotFound No value associated with key CodingKeys

This is a common mistake. You are ignoring the root object, the dictionary with key answer which of course doesn't have a key id

Add this struct

struct Root: Decodable {
let answer : Answer
}

and decode

let answerCreateResponse = try JSONDecoder().decode(Root.self, from: data).answer

No value associated with key CodingKeys error

you are trying to decode a Book object, where you should be decoding the
API response, then extract the Books from that. This is why you are getting the error.

Try the following example code:

struct Book: Identifiable, Codable {
let id = UUID()
let volumeInfo: VolumeInfo
}

struct VolumeInfo: Codable {
let title, publishedDate: String
let authors: [String]?
let publisher, description: String?

}

struct ApiResponse: Codable {
let kind: String
let totalItems: Int
let items: [Book]
}

var bookInfo = [Book]()

override func viewDidLoad() {
super.viewDidLoad()
if let url = URL(string: "https://www.googleapis.com/books/v1/volumes?q=harry+potter") {
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
do {
let response = try JSONDecoder().decode(ApiResponse.self, from: data)
bookInfo = response.items
} catch {
print(error)
}
}
}.resume()
}
}

When to use CodingKeys in Decodable(Swift)

First of all there is a make-or-break rule for using CodingKeys:

  • You can omit CodingKeys completely if the JSON – or whatever Codable conforming format – keys match exactly the corresponding properties (like in your example) or the conversion is covered by an appropriate keyDecodingStrategy.

  • Otherwise you have to specify all CodingKeys you need to be decoded (see also reason #3 below).


There are three major reasons to use CodingKeys:

  1. A Swift variable/property name must not start with a number. If a key does start with a number you have to specify a compatible CodingKey to be able to decode the key at all.
  2. You want to use a different property name.
  3. You want to exclude keys from being decoded for example an id property which is not in the JSON and is initialized with an UUID constant.

And CodingKeys are mandatory if you implement init(from decoder to decode a keyed container.

Nested Swift 4 Codable Struct uses CodingKeys irregularly

Apple doc clearly states in the first sentence on the paragraph below. A special enumeration named 'CodingKeys' is necessary. (I had the similar problem and took me quite some time to find out).

Sample Image
https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types

Codable class doesn't conform to protocol 'Decodable'

It is not necessary to separate the structs. The error occurs because you have added too many keys in the CodingKeys enum. If you keep only the required ones, then it will work:

struct Model: Codable {
let aps: Aps
let link: String?
let checkAction: String?

enum CodingKeys: String, CodingKey {
case aps ,link
case checkAction = "gcm.notificaiton.check_action"
}

struct Aps: Codable {
let alert: Alert
let sound: String?


struct Alert: Codable {
let title: String?
let body: String?
}
}
}

alert, sound etc are not coding keys of Model. They are coding keys of Aps. It is not necessary to specify them in Aps, because they are the same as the property names.

Swift custom decodable initializer without CodingKeys

The auto-generation for CodingKeys is really weird. The scope and availability of it changes based on what members you have.

Say you just have a Decodable. These compile:

struct Decodable: Swift.Decodable {
static var codingKeysType: CodingKeys.Type { CodingKeys.self }
}
struct Decodable: Swift.Decodable {
static func `init`(from decoder: Decoder) throws -> Self {
_ = CodingKeys.self
return self.init()
}
}

…and you can put them together, if you add private.

struct Decodable: Swift.Decodable {
private static var codingKeysType: CodingKeys.Type { CodingKeys.self }

static func `init`(from decoder: Decoder) throws -> Self {
_ = CodingKeys.self
return self.init()
}
}

…But make that func an initializer, and again, no compilation.

struct Decodable: Swift.Decodable {
private static var codingKeysType: CodingKeys.Type { CodingKeys.self }

init(from decoder: Decoder) throws {
_ = CodingKeys.self
}
}

You can change it to be fully Codable, not just Decodable

struct Decodable: Codable {
private static var codingKeysType: CodingKeys.Type { CodingKeys.self }

init(from decoder: Decoder) throws {
_ = CodingKeys.self
}
}

But then you can't use CodingKeys at type scope, so the property won't compile.

Considering you probably don't need such a property, just use Codable, file a bug with Apple referencing this answer, and hopefully we can all switch to Decodable when they fix it. /p>

Make Swift JSONDecoder not fail when type on key not match

Implement init(from:) in struct Object. Create enum CodingKeys and add the cases for all the properties you want to parse.

In init(from:) parse the keys manually and check if year from JSON can be decoded as an Int. If yes, assign it to the Object's year property otherwise don't.

struct Object: Codable {
var year: Int?

enum CodingKeys: String,CodingKey {
case year
}

init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
if let yr = try? values.decodeIfPresent(Int.self, forKey: .year) {
year = yr
}
}
}

Parse the JSON response like,

do {
let object = try JSONDecoder().decode(Object.self, from: data)
print(object)
} catch {
print(error)
}

Example:

  1. If JSON is { "year": "10"}, object is: Object(year: nil)
  2. If JSON is { "year": 10}, object is: Object(year: Optional(10))


Related Topics



Leave a reply



Submit