SomeGuyFortune
SomeGuyFortune

Reputation: 1056

Convert swift struct to json string

I am trying to convert my swift struct to json format. There seems to be quite a few questions like this, but none of the solutions has worked for me so far.

Here is my struct

struct Rec : Codable {
    var name:String
    var time:Int
    var values:[String]

    var jsonRepresentation : String {
        return """
            {
                "name":"\(name)",
                "time":\(time),
                "values":\(values)
            }
        """
    }
}

Here is the json format that the external api recieves.

{
    "op" : "delete",
     "rec": { 
        "name" : "some string",
        "time":200,

        "values": [ 
            "127.0.0.1"
        ]
    }
}

Here is my function the creates the HTTP request

 private func createPatchRequest(op: String, url: URL, rec: Rec) throws -> URLRequest {
    let dictionary: [String: String] = [
        "op":op,
        "rec":rec.jsonRepresentation
    ]

    let jsonData = try JSONEncoder().encode(dictionary)

    var request = URLRequest(url: url)

    request.httpMethod = "PATCH"
    request.httpBody = jsonData
    request.addValue("application/json", forHTTPHeaderField:"Content-Type")

    return request
}

I always get a 400 and i believe that swift isn't' encoding my json properly. I tried the request on postman and it worked fine!

Here is how it looked on postman

{
    "op" : "delete",
    "rec": { 
        "name" : "some string",
        "time":600,
        "values": [ 
            "127.0.0.1"
        ]
    }
}

Any ideas? Thanks!

Upvotes: 2

Views: 9030

Answers (2)

W.K.S
W.K.S

Reputation: 10095

The problem is that the API expects rec to be a JSON object, but you're sending Rec as a string representation of a JSON Object. i.e. This is the JSON that you're sending:

{
  "rec": "{\n\"name\":\"some string\",\n\"time\":1,\n\"values\":[\"127.0.0.1\"]\n}",
  "op": "delete"
}

The ideal approach would be to create a Codable PatchRequest with a nested Rec struct and encode that. You can try the following snippet on Playground

struct Rec : Codable {
    var name:String
    var time:Int
    var values:[String]
}
struct PatchRequest : Codable{
    var op: String
    var rec: Rec
}

let rec = Rec(name: "some string",time: 600,values: ["127.0.0.1"])
let request = PatchRequest(op: "delete",rec: rec)

let jsonData = try JSONEncoder().encode(request)
let jsonString = String(data: jsonData, encoding: .utf8) 

This has the added benefit of reducing the number of parameters required by your API method and making the code cleaner overall:

private func createPatchRequest(url: URL, patchRequest: PatchReuest) throws -> URLRequest {
    let jsonData = try JSONEncoder().encode(patchRequest)
    
    var request = URLRequest(url: url)
    
    request.httpMethod = "PATCH"
    request.httpBody = jsonData
    request.addValue("application/json", forHTTPHeaderField:"Content-Type")
    
    return request
}

Lastly, I would recommend using let in your structs instead of var unless it's absolutely necessary.

Upvotes: 7

Tung Fam
Tung Fam

Reputation: 8147

Your jsonRepresentation is a string but should be a dictionary. Update your struct this way:

struct Rec : Codable {
var name:String
var time:Int
var values:[String]

var jsonRepresentation: [String: Any] {
    return [
        "name": name,
        "time": time,
        "values": values
    ]
  }
}

Hope it helps! Please let me know if it worked.

Upvotes: 1

Related Questions