'How to send nested raw JSON in URLSession to Vapor API?

I have a Vapor API that has a route to register users. This route recibes a nested object in JSON format like this:

{
    "name": "Test",
    "email": "[email protected]",
    "password": "Test1",
    "phone": {
        "numberString": "+52 999 999 9999",
        "countryCode": 52,
        "nationalNumber": 9999999999,
    }
}

This JSON is converted into a Content/Codable Object:

final class Create: Codable, Content {
    let name: String!
    let email: String!
    let password: String
    let phone: PhoneNumber!
    
    init(name: String, email: String, password: String, phone: PhoneNumber) {
        self.name = name
        self.email = email
        self.password = password
        self.phone = phone
    }
}

I have tried this route sending the JSON string as raw via Postman and the route worked perfectly but the problem is when I try to send it via URLSession in my iOS counterpart the ErrorMiddleware throws a DecodingError:

DecodingError: Value of type 'String' required for key 'password'.

At first I thought that the problem was the JSON generation until, for test purpose I send the same JSON as in the example and the Vapor API is still throwing the Error.

let urlStr = "\(BaseURL)/api/student/register"

guard let url = URL(string: urlStr) else { return }

var urlRequest = URLRequest(url: url, cachePolicy:
    .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 30.0)


let raw = "{\"name\":\"Prueba\",\"email\":\"[email protected]\",\"password\":\"Prueba1\",\"phone\":{\"numberString\":\"\",\"countryCode\":,\"nationalNumber\":}}"

urlRequest.httpMethod = "POST"
urlRequest.httpBody = raw.data(using: .utf8)

urlRequest.addValue("application/json", forHTTPHeaderField: "Accept")

URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
    ...
}.resume()

Can you even send this types of JSON's via URLSession or do I need to change my logic so it will receive a flat array?



Solution 1:[1]

After hours of debugging and getting strange errors I realized that my error was simpler than I thought. The error was:

{"error":true, "reason":"Value of type 'String' required for key 'password'."}

And I tried to send in Postman a request with out the key 'password' which returned:

{"error": true, "reason": "Value required for key 'password'."}

What got me to analyze my objects and then I saw the error, my Create object wasn't unwrapped correctly, more precisely this:

let password: String

Should be having a ! next to String like this.

let password: String!

The reason why this worked on Postman and not on URLSession is still uncleared.

UPDATE:

As proposed by @vadian the headers in this URLSession are also missing and even tho after I added the force unwrapped the API passed the request but with nil content

 urlRequest.setValue("application/json", forHTTPHeaderField:"Accept")
 urlRequest.setValue("application/json", forHTTPHeaderField:"Content-Type")

Solution 2:[2]

Set also the content-type and length header

 urlRequest.setValue(String(Data(json.utf8).count), forHTTPHeaderField:"Content-Length")
 urlRequest.setValue("application/json", forHTTPHeaderField:"Accept")
 urlRequest.setValue("application/json", forHTTPHeaderField:"Content-Type")

And never, never, never declare properties in a class as implicit unwrapped optional which are initialized with non-optional values.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 vadian