0

I have constructed this JSON using a fair amount of string interpolation:

{
    "headers":{
        "email":"email@example.org",
        "rank":0,
        "blue":false,
        "team":1000,
        "round":33,
        "tournament_id":"7F98sdh98aFH98h"
    },
    "data":{
        "start_position":0.0,
        "crossed_line":true,
        "end_platform":true,
        "lift":0,
        "first-actions":[
            {
                "timestamp":1520403299.17746,
                "action":"0_0_1"
            }
        ],
        "second-actions":[
            {
                "timestamp":1520403299.96991,
                "action":"0_0_2"
            }
        ]
    }
}

I tried to include this in my POST request's httpBody like so:

request.httpBody = json.data(using: .utf8)

However, this results in a 422 error.

Serverside, the result is all of the string being interpreted as a single header:

--- NEW REQUEST ---
Time: March 6th 2018, 8:47:23 pm (1520398043327)
IP:  [REDACTED]
Request: [REDACTED]
req.body = {'{"headers":{"email":"email@example.org","rank":0,"blue":false,"team":1000,"round":20,"tournament_id":"7F98sdh98aFH98h",},"data":{"start_position":-36.5385,"crossed_line":true,"end_platform":true,"lift":0,"first-actions":[{"timestamp":1520398021.45196,"action":"0_0_1"}],"second-actions":[{"timestamp":1520398022.73314,"action":"0_0_2"}]}}':''}
Auth: [REDACTED]
Auth level: 10

I then realised that it should be sent as a JSON Object, not a string. I have tried many ways of doing this, including converting json to a Dictionary, but then converting that to Data gives a runtime error.

How should I go about converting the string to the correct format?

EDIT: Result from Dávid's answer:

--- NEW REQUEST: 60 ---
[REQ 60] Time: March 7th 2018, 8:52:39 pm (1520484759369)
[REQ 60] IP: [REDACTED]
[REQ 60] Request: [REDACTED]
[REQ 60] req.body = { '{"headers":{"team":"1000","email":"email@example.org","rank":"0","blue":"false","round":"22","tournament_id":"7F98sdh98aFH98h"},"data":{"lift":"0","crossed_line":"true","end_platform":"true","first-actions":[{"timestamp":0,"action":"0_0_0"},{"timestamp":1520484747.061681,"action":"0_0_1"}],"second-actions":[{"timestamp":0,"action":"0_0_0"},{"timestamp":1520484747.9255838,"action":"0_0_2"}],"start_position":"0.0"}}': '' }
Auth: [REDACTED]
atirit
  • 1,492
  • 5
  • 18
  • 30

1 Answers1

1

First of all, you shouldn't be using String interpolation to create JSON objects, but rather create the actual object you want to send, which in your case is a Dictionary.

Once you have the data you want to send stored in a variable of type Dictionary<String,Any>, you can convert it to JSON using the JSONSerialization or JSONEncoder APIs.

let email = "email@example.org"
let dictionary = ["headers":["email":email,"rank":0, "blue":false,"team":1000,"round":33, "tournament_id":"7F98sdh98aFH98h"], "data":[ "start_position":0.0, "crossed_line":true, "end_platform":true, "lift":0, "first-actions":[["timestamp":1520403299.17746,"action":"0_0_1"]],"second-actions":[[ "timestamp":1520403299.96991, "action":"0_0_2"]]]]
do {
    let jsonEncodedDictionary = JSONSerialization.data(withJSONObject: dictionary)
    request.httpBody = jsonEncodedDictionary
} catch {
    //You should handle the errors more appropriately for your specific use case
    print(error)
}
Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
  • How would I construct a dictionary that would contain my JSON? – atirit Mar 07 '18 at 22:21
  • @atirit check my updated answer. You just need to move all dynamic content to variables, then use the variables to assign keys/values in your `Dictionary` just as I did with `email`. – Dávid Pásztor Mar 07 '18 at 22:53
  • Is there any way for me to append more `["timestamp":1520403299.96991, "action":"0_0_2"]`s? Doing `dictionary["data"]!["first-actions"].append(dataToAppend)` throws `Value of type 'Any?' has no member 'append'`. – atirit Mar 08 '18 at 00:25
  • You should seriously look into how to handle dictionaries in Swift, since it's quite a simple thing to do and it seems you lack the basics on the topic. Since dictionary as most other native collections in Swift is homogenous, the compiler inferred the type of `dictionary` to `[String:[String:Any]]` here. You need to cast `data` to an array to able to append it. `(dictionary["data"]!["first-actions"] as! Array<[String:Any]>).append(dataToAppend)` – Dávid Pásztor Mar 08 '18 at 00:36
  • With your code I receive `Cannot use mutating member on immutable value of type 'Array<[String : Any]>'`. In addition, nowhere in the docs is it mentioned that most collections are homogeneous; I had no reason to assume that an explicit cast was necessary. – atirit Mar 08 '18 at 01:07
  • @atirit you're right, that intermediate casting can't work, you'll need to create a temporary variable, then assign that to the `first-actions` key in your dictionary like `let dataToAppend = ["timestamp":1520403299.177431,"action":"0_0_2"] as [String : Any] ; var firstActions = dict["data"]!["first-actions"] as! Array<[String:Any]>; firstActions.append(dataToAppend); dict["data"]!["first-actions"] = firstActions` – Dávid Pásztor Mar 08 '18 at 01:26
  • I get the same response from the server when this is implemented, where everything is under one argument with no value. Edited OP to include the response I get. – atirit Mar 08 '18 at 04:57
  • @atirit have you managed to get a request working from any other environment? You should get the request to work from Postman, then include the values you used in this question so that we can see what differences might be between the request in Swift and the one that succeeded in Postman. – Dávid Pásztor Mar 08 '18 at 10:47