Response struct does not like CodingKeys [closed] - swift

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 1 year ago.
Improve this question
I have this struct used to decode a JSON coming from the server:
struct AdminResponse: Codable {
let Status: Int?
let SuperUsers: [SuperUser]?
let Message: String?
}
struct SuperUser: Codable {
let name:String?
let id:String?
}
This works perfectly. I am able to decode the JSON.
But I don't like these properties with the first uppercase, then I do
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"
}
}
struct SuperUser: Codable {
let name:String?
let id:String?
enum CodingKeys: String, CodingKey {
case name = "name"
case id = "id"
}
}
Error: Type AdminResponse does not conforme to protocol Decodable.
Why???

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"
}
}

Related

When to use CodingKeys in Decodable(Swift)

Let's say I want to decode a Person struct as follows.
struct Person: Decodable {
let firstName: String
let lastName: String
let age: Int: String
}
I understand that the data can be decoded only with above. Therefore if I'm not changing the properties to a custom name if there no difference between the above and below implementation?
Further is there other cases where you want to use CodingKeys? I'm confused when they are necessary other than for renaming purposes.
struct Person: Decodable {
let firstName: String
let lastName: String
let age: Int: String
}
enum CodingKeys: String, CodingKey {
case firstName
case lastName
case age
}
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:
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 an UUID constant.
And CodingKeys are mandatory if you implement init(from decoder to decode a keyed container.
You can use CodingKeys in different ways for example, when you know that at least one of the name of values that you are expecting in your JSON is actually different from your "let or var" name.
Example:
struct Person: Decodable {
let firstName: String
let lastName: String
let age: Int: String
}
enum CodingKeys: String, CodingKey {
case firstName = "first_name"
case lastName
case age
}
Other case is when you are using class inheritance.
In conclusion, if you are absolutely sure that you are using the same variable name as your encoding key(JSON), you can omit it (but if you want to put it, it doesn't matter), but if there's a difference, maybe a change of your codingKeys like an uppercase or using different words, you should use the enum to map the correct key with the variable name.
CodingKeys can be extremely helpful if you have a JSON with arbitrary number of coding keys (also called dynamic keys). Here is an example.
import UIKit
// Consider JSON with infinite number of keys: "S001", "S002" and so on
let jsonData = """
{
"S001": {
"firstName": "Tony",
"lastName": "Stark"
},
"S002": {
"firstName": "Peter",
"lastName": "Parker"
},
"S003": {
"firstName": "Bruce",
"lastName": "Wayne"
}
}
""".data(using: .utf8)!
struct Student: Decodable {
let firstName: String
let lastName: String
}
struct DecodedArray: Decodable {
var array: [Student]
// Define DynamicCodingKeys type needed for creating
// decoding container from JSONDecoder
private struct DynamicCodingKeys: CodingKey {
// Use for string-keyed dictionary
var stringValue: String
init?(stringValue: String) {
self.stringValue = stringValue
}
// Use for integer-keyed dictionary
var intValue: Int?
init?(intValue: Int) {
// We are not using this, thus just return nil
return nil
}
}
init(from decoder: Decoder) throws {
// 1
// Create a decoding container using DynamicCodingKeys
// The container will contain all the JSON first level key
let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
var tempArray = [Student]()
// 2
// Loop through each key (student ID) in container
for key in container.allKeys {
// Decode Student using key & keep decoded Student object in tempArray
let decodedObject = try container.decode(Student.self, forKey: DynamicCodingKeys(stringValue: key.stringValue)!)
tempArray.append(decodedObject)
}
// 3
// Finish decoding all Student objects. Thus assign tempArray to array.
array = tempArray
}
}
let decodedResult = try! JSONDecoder().decode(DecodedArray.self, from: jsonData)
Therefore if I'm not changing the properties to a custom name if there no difference between the above and below implementation?
Yes, but there's a bit of misunderstanding here. The two implementations you have are literally identical because in the second one the CodingKeys enum would never be used. To be used, the enum must be nested within the Decodable conforming type (Person in this case):
struct Person: Decodable {
let firstName: String
let lastName: String
let age: Int
enum CodingKeys: String, CodingKey {
case firstName
case lastName
case age
}
}
There is in practice no difference between this implementation and the ones you provided.
Further is there other cases where you want to use CodingKeys?
CodingKeys are not used solely by Decodable, they are also used by Encodable. When using Encodable, a reason to use CodingKeys is to specify only a subset of the instances fields should be serialized.

Can't use #available unavailable with Codable

I would like to apply the available attribute with the renamed and unavailable arguments to a property of struct that conforms to Codable , as shown below:
struct SampleData: Codable {
#available(*, unavailable, renamed: "newProperty")
let oldProperty: String
let newProperty: String
}
But when I tried to build this code , I got a compile error like this:
note: 'oldProperty' has been explicitly marked unavailable here
If a struct does not conform to Codable, it works well.
Does anyone know how to resolve this problem?
And if it is impossible to resolve this, I'd appreciate it if you could tell me why.
Thanks in advance.
This is because the synthesised Codable conformance is trying to decode/encode oldProperty as well. It can't not do that, because all stored properties has to be initialised, even if they are unavailable.
It will work if you initialise oldProperty to some value, and add a CodingKey enum to tell the automatically synthesised conformance to only encode/decode newProperty:
struct SampleData: Codable {
#available(*, unavailable, renamed: "newProperty")
let oldProperty: String = ""
let newProperty: String
enum CodingKeys: CodingKey {
case newProperty
}
}
Actually, depending on the situation, you might be able to convert oldProperty to a computed property, in which case you don't need the coding keys.
struct SampleData: Codable {
#available(*, unavailable, renamed: "newProperty")
var oldProperty: String { "" }
let newProperty: String
}

Get the coding key for a keypath in Swift [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
What I have: A codable struct with different properties.
What I want: A function, where I can get the exact name of the property when it is encoded in a Json. I think the most promising approach is using Keypath, but I have no idea how and whether it is possible at all. Thanks!
There is no way to do this out of the box, since there's no 1-1 mapping between properties of a Codable type and its coding keys, since there might be properties that aren't part of the encoded model or properties that depend on several encoded keys.
However, you should be able to achieve your goals by defining a mapping between the properties and their coding keys. You were on the right track with KeyPaths, you just need to define a function that takes a KeyPath whose root type is your codable model and return the coding key from said function.
struct MyCodable: Codable {
let id: Int
let name: String
// This property isn't part of the JSON
var description: String {
"\(id) \(name)"
}
enum CodingKeys: String, CodingKey {
case name = "Name"
case id = "identifier"
}
static func codingKey<Value>(for keyPath: KeyPath<MyCodable, Value>) -> String? {
let codingKey: CodingKeys
switch keyPath {
case \MyCodable.id:
codingKey = .id
case \MyCodable.name:
codingKey = .name
default: // handle properties that aren't encoded
return nil
}
return codingKey.rawValue
}
}
MyCodable.codingKey(for: \.id)

Is there a way to extract name of case of CodingKeys enum?

I'm trying to access the CodingKeys enum case's name. The only thing I can access to is its stringValue. I need to know the name because I comparing it to the variable name in the Decodable object. And sometimes the stringValue and the actual name is different.
I am aware that I could manually write a variable that would return the name of each case...but that wouldn't be scalable as it would make the models have unnecessary amount of boiler plate code.
This is basic example where the name is different from the stringValue.
I need to somehow extract the name.
#objcMembers
class User: Decodable {
dynamic var name: String = ""
enum CodingKeys: String, CodingKey {
case name = "user_name"
}
}
if you have a instance of your enum, you can convert it's name to string like this:
let myEnum : CodingKeys = .name
let stringRepresentation = "\(myEnum.self)"
print(stringRepresentation) //this will print "name" on the console

Using Swift 4 Codable Protocol with Unknown Dictionary Keys

I am working with NASA's Near Earth Object Web Service to retrieve data to be displayed in an application. I understand how to use Swift 4's Codable protocol, but I do not understand how to map part of the response.
Using Paw, I inspected the response from the API:
As you can see, the near_earth_objects structure is a Dictionary, and the keys are dates. The issue is that the URL parameters are dates, so these date structures will change, depending on the day of the request. Therefore, I do not know how I can create properties to be automatically mapped when using the Codable protocol.
The data that I am trying to get to inside of these structures are Arrays that contain Dictionarys:
How can I have my model object conform to the Codable protocol and map these structures when the dates will change as the dates of the requests change?
You don't need to know the keys of the Dictionary compile time if you don't mind keeping a Dictionary after decoding.
You just need to specify the property with type Dictionary<String:YourCustomDecodableType>. The keys will be dates corresponding to observation and the value will an array of all objects with your custom type.
struct NearEarthObject: Codable {
let referenceID:String
let name:String
let imageURL:URL
private enum CodingKeys: String, CodingKey {
case referenceID = "neo_reference_id"
case name
case imageURL = "nasa_jpl_url"
}
}
struct NEOApiResponse: Codable {
let nearEarthObjects: [String:[NearEarthObject]]
private enum CodingKeys: String,CodingKey {
case nearEarthObjects = "near_earth_objects"
}
}
do {
let decodedResponse = try JSONDecoder().decode(NEOApiResponse.self, from: data)
} catch {
print(error)
}
As you said, near_earth_objects is a Dictionary, but keys are not Dates, keys are Strings, and values are arrays of the known structures. So the above code will work:
...
let nearEarthObjects: [String: [IndexObject]]
...
enum CodingKey: String, CodingKeys {
case nearEarthObjects = "near_earth_objects"
}
struct IndexObject: Decodable {
...
let name: String
...
}