Issue with networking and Codable class

Hello,
I’m having a little issue with my Codable class…
I have an Int which I will Post on my API. But when I GET back, it is a String.
Let me show you some code:
  • this is my class:

Code Block Swift
class NFCDataSec: ObservableObject, Codable {
enum CodingKeys: String, CodingKey {
case firstName, lastName, age
}
@Published var lastName: String = ""
@Published var firstName: String = ""
@Published var age: Int = 0
init() { }
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
lastName = try container.decode(String.self, forKey: .lastName)
firstName = try container.decode(String.self, forKey: .firstName)
age = try container.decode(Int.self, forKey: .age) /* (1) */
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(firstName, forKey: .firstName)
try container.encode(lastName, forKey: .lastName)
try container.encode(age, forKey: .age)
}
}
  • this is my JSON Post request:

Code Block Swift
        func SecuringData() throws {
guard let encoded = try? JSONEncoder().encode(dataToWrite) else {
print("Fail to encode SecuringData - NFCDataSec")
return
}
let url = URL(string: "https://MY_API.COM/api/sendToChip")!
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.httpBody = encoded
URLSession.shared.dataTask(with: request) { data, res, error in
guard let httpResponse = res as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
self.handleServerError(res)
return
}
if let data = data {
let decoder = JSONDecoder()
if let json = try? decoder.decode(NFCDataSec.self, from: data) {
print(json)
}
else {
let dataString = String(decoding: data, as: UTF8.self)
print("Invalid response \(dataString)") /* (2) */
}
}
}.resume()


(1) the API GET returns String instead of Int - but it needs to have Int when I’m making the POSTmethod
(2) I get an invalid response from the API when I GET the data back from it.

Here is an exemple of working JSON
  • POST:

Code Block
{
"lastName":"John",
"firstName":"Taylor",
"age":23
}
  • GET:

Code Block
{
"firstName": "8ac18f61",
"lastName": "88cf8f64dc719eac6a",
"age": "e893"
}

Answered by Paul_a_Dance in 656203022

If you expect dataToWrite to be updated, you need to explicitly update it in the completion handler of dataTask.

If I understand correctly and to be sure, I need to to it in this part of the function, where I check the server's response ?
Code Block Swift
if let data = data {
    let decoder = JSONDecoder()
    print("this is what i'm retreiving from the API \(String(decoding: data, as: UTF8.self))")
    if let json = try? decoder.decode(NFCDataSec.self, from: data) {
        print(json)
        print(self.dataToWrite.firstName)
    }
}

And I need to do like so ?
Code Block Swift
    if let json = try? decoder.decode(NFCDataSec.self, from: data) {
        print(json)
        self.dataToWrite.firstName = json.firstName
    }

It depends on what the API does, but, if the API returns String, you may need to prepare a type accepting String.

Add another struct to receive the value from the API:
Code Block
struct NFCDataSecAPI: Codable {
var lastName: String = ""
var firstName: String = ""
var age: String = "0"
}

Or use only this one and change other parts of your code.
I created a new variable @Published var stringAge: String = "" and edited my decoder with this new variable, and it is fixed.
Code Block Swift
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
lastName = try container.decode(String.self, forKey: .lastName)
firstName = try container.decode(String.self, forKey: .firstName)
/* age = try container.decode(Int.self, forKey: .age) */
stringAge = try container.decode(String.self, forKey: .age)
}

Here is a piece of mu function:
Code Block Swift
if let data = data {
                    let decoder = JSONDecoder()
                    print("this is what i'm retreiving from the API \(String(decoding: data, as: UTF8.self))")
                    if let json = try? decoder.decode(NFCDataSec.self, from: data) {
                        print(json) /* (1) */
                        print(self.dataToWrite.firstName) /* (2) */
                    }

Here is my console log:
Code Block JSON
Encoded data:
{"lastName":"John","firstName":"Paul","age":23}
this is what i'm retreiving from the API
{"firstName":"504ae2d3","lastName":"5244e2d626d03a7e8f","age":"3218"}
Healthsafe.NFCDataSec /* (1) */
Paul /* (2) */

I can't understand, now, why my class is not updated with the retrieved data (2).

Could it be because I'm not using the main thread to POSTand GET from the API ? 

I do not think so. You code is disposing the most important error info, so nothing sure.


(UPDATE)
Seems you have updated your last post.

I can't understand, now, why my class is not updated with the retrieved data (2).

Where is the code updating your class?

Seems you have updated your last post.

Yes sorry, I updated it because I forgot to change a type and it worked just after posting the first version. I forgot to tell it was updated.

Where is the code updating your class?

I thought by doing this line it would automatically update my class with the resent values
Code Block Swift
if let json = try? decoder.decode(NFCDataSec.self, from: data)



I thought by doing this line it would automatically update my class with the resent values

As a fact you can see, it does not.

If you expect dataToWrite to be updated, you need to explicitly update it in the completion handler of dataTask.
Accepted Answer

If you expect dataToWrite to be updated, you need to explicitly update it in the completion handler of dataTask.

If I understand correctly and to be sure, I need to to it in this part of the function, where I check the server's response ?
Code Block Swift
if let data = data {
    let decoder = JSONDecoder()
    print("this is what i'm retreiving from the API \(String(decoding: data, as: UTF8.self))")
    if let json = try? decoder.decode(NFCDataSec.self, from: data) {
        print(json)
        print(self.dataToWrite.firstName)
    }
}

And I need to do like so ?
Code Block Swift
    if let json = try? decoder.decode(NFCDataSec.self, from: data) {
        print(json)
        self.dataToWrite.firstName = json.firstName
    }

And I need to do like so ?

Nearly, yes. Depends on the definition of dataToWrite.
(And I would never use try? in this case. Better use do-try-catch.)

Nearly, yes. Depends on the definition of dataToWrite.
It works, the data are stored in the class.



I have a question a bit similar, but I can't figure out the issue.
Let me explain: I have a connection URL where I send my id and password and I'm supposed to GET id, sessionID and token but I always receive an invalid response (it is the same code as above). When I send the JSON with Postman I have no error ...

I have a question a bit similar, but I can't figure out the issue.

Ignoring a reply for 1 week and post another question???

...

You should start a new thread for your new question, better include a link to this thread.
Issue with networking and Codable class
 
 
Q