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 whateverCodable
conforming format – keys match exactly the corresponding properties (like in your example) or the conversion is covered by an appropriatekeyDecodingStrategy
.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
:
- 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. - You want to use a different property name.
- You want to exclude keys from being decoded for example an
id
property which is not in the JSON and is initialized with anUUID
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).
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:
- If JSON is
{ "year": "10"}
,object
is:Object(year: nil)
- If JSON is
{ "year": 10}
,object
is:Object(year: Optional(10))
Related Topics
How to Specify the Type Information of an Array That Would Hold Swiftui Custom Views
How to Set Scrollview Content Size in Swift 3.0
What Are the Fundamental Differences Between Set and Didset
Cannot Call Value of Non-Function Type 'Ciimage'
Swift Throws Python Errors from Terminal
Exc_Bad_Instruction Happens When Using Dispatch_Get_Global_Queue on iOS 7(Swift)
Change a Dictionary's Key in Swift
Explicitly Unwrapping Optional Nil Does Not Cause Crash
Does the Initializer of an 'Open' Class Need to Be Open as Well
Swift: Label Text --> "Fatal Error: Unexpectedly Found Nil While Unwrapping an Optional Value"
How to Check If a Type Is Optional in Swift
How to Convert a Unsafemutablepointer<Void> to Uint8
Why Is Casting a Struct to Anyobject Not a Compile Error in Swift
Cannot Call Value of Non-Function Type 'Nshttpurlresponse' Alamofire Objectmapper
Self' Is Only Available in a Protocol or as the Result of a Class Method
How to Access Modifiers of a View in Swiftui
How to Compare Range<String.Index> and Defaultbidirectionalindices<String.Characterview>