0

I am having problems with sending a list of dictionaries as the value for one of my keys in a dictionary. I'm sending a dictionary that represents a game, with one of the key value pairs being a list of points. So for my 'points' key the corresponding value is a list of dictionaries.

var allPoints : [[String: String] = []
for playerScoreTracker in playerScoreTrackers!{
    for point in playerScoreTracker.points{
        let tempDict : [String : String] = ["type": point.typeOfPoint.rawValue, "scorer" : point.scorer.uuid]
        allPoints.append(tempDict)
    }
}
    
var parameters : Dictionary<String, Any> = [
    "playerOne": playerOne.uuid,
    ...
    "team_two_score": scoreboard.teamTwoScore,
    "confirmed": false,
    "points": allPoints
]

The problem is that when I send this dictionary using Alamofire (which uses JSONEncoding.default to my knowledge) it ends up sending something different than I want. My Alamofire send function looks like this

static func post<T: Decodable>(url: String, parameters: [String: Any], returnType: T.Type, completion: @escaping (Bool, Any) -> Void) {
    AF.request(url, method: .post, parameters: parameters, headers: getHeaders()).responseDecodable(of: returnType.self) { response in
        guard let returnStatusCode = response.response?.statusCode else {
            let returnData : [String] = ["No connection."]
            completion(false, returnData)
            return
        }
        switch returnStatusCode {
        case 200, 201:
            let returnData = response.value!
            completion(true, returnData)
        case 400:
            var errorsDict : [String: [String]] = [:]
            do {
                errorsDict = try JSONDecoder().decode(Dictionary<String, [String]>.self, from: response.data!)
            } catch {
                errorsDict["my_side_errors"] = ["Couldn't decode json response."]
            }
            let errors : [String] = getErrorsFromParams(params: parameters, errorsDict: errorsDict)
            completion(false, errors)
        default:
            let errors : [String] = ["Error."]
            completion(false, errors)
        }
    }
}

For some reason when it encodes my parameters it sends them like this...

{
    'confirmed': ['0'], 
    'playerFour': ['8b52d186-ab60-438e-92f1-a3d35430ea81'], 
    'playerOne': ['f33e09b5-be8b-49e7-99c2-1e2df7512a71'], 
    'playerThree': ['583e0143-7352-4907-a806-a3b16baceefc'], 
    'playerTwo': ['24da0653-7fbc-41c3-8a47-bdefb9323eb2'], 
    'points[][type]': ['tk', 'tk'], 
    'points[][scorer]': ['8b52d186-ab60-438e-92f1-a3d35430ea81', '8b52d186-ab60-438e-92f1-a3d35430ea81'], 
    'team_one_score': ['12'], 
    'team_two_score': ['0'], 
    'time_ended': ['2021-09-11 10:54:00'], 
    'time_started': ['2021-09-11 10:53:57']
}

The list of dictionaries I send as points is being split up into two lists, one list for each key in the array.

If I send the same JSON in Postman, it works fine and exactly how I want it to. This is the JSON I send it in Postman...

{
    "playerOne": "f33e09b5-be8b-49e7-99c2-1e2df7512a71", 
    "playerTwo": "99028402-abdd-42dd-a8d9-48c05ba2787e", 
    "type": "pu", 
    "team_two_score": 0, 
    "time_started": "2021-09-11 12:33:44", 
    "time_ended": "2021-09-11 12:33:48", 
    "points": [
        {"scorer": "f33e09b5-be8b-49e7-99c2-1e2df7512a71", "type": "tk"}, 
        {"scorer": "f33e09b5-be8b-49e7-99c2-1e2df7512a71", "type": "tk"}, 
        {"type": "tk", "scorer": "f33e09b5-be8b-49e7-99c2-1e2df7512a71"}, 
        {"type": "tk", "scorer": "f33e09b5-be8b-49e7-99c2-1e2df7512a71"}, 
        {"type": "tk", "scorer": "f33e09b5-be8b-49e7-99c2-1e2df7512a71"}, 
        {"scorer": "f33e09b5-be8b-49e7-99c2-1e2df7512a71", "type": "tk"}
    ],
    "playerThree": "24da0653-7fbc-41c3-8a47-bdefb9323eb2", 
    "playerFour": "583e0143-7352-4907-a806-a3b16baceefc", 
    "team_one_score": 12, 
    "confirmed": false
}

This JSON being sent works fine with my backend. I cannot for the life of me figure out how to get Swift to encode my params in this way.

Here's a printout of the parameters right before I send them.

params: [
    "team_one_score": 13, 
    "points": [
        ["type": "tk", "scorer": "f33e09b5-be8b-49e7-99c2-1e2df7512a71"], 
        ["scorer": "f33e09b5-be8b-49e7-99c2-1e2df7512a71", "type": "tk"], 
        ["type": "tk", "scorer": "f33e09b5-be8b-49e7-99c2-1e2df7512a71"], 
        ["type": "tk", "scorer": "f33e09b5-be8b-49e7-99c2-1e2df7512a71"], 
        ["type": "tk", "scorer": "f33e09b5-be8b-49e7-99c2-1e2df7512a71"], 
        ["scorer": "f33e09b5-be8b-49e7-99c2-1e2df7512a71", "type": "sk"]
    ], 
    "time_started": "2021-09-11 12:12:26", 
    "confirmed": false, 
    "time_ended": "2021-09-11 12:12:30", 
    "playerThree": "583e0143-7352-4907-a806-a3b16baceefc",
    "team_two_score": 0, 
    "playerTwo": "24da0653-7fbc-41c3-8a47-bdefb9323eb2",
    "playerFour": "8b52d186-ab60-438e-92f1-a3d35430ea81",
    "playerOne": "f33e09b5-be8b-49e7-99c2-1e2df7512a71"
]

Here's the output from cURLDescription

$ curl -v \
    -X POST \
    -H "User-Agent: DayOfDie/1.0 (aberard.DayOfDie; build:1; iOS 14.5.0) Alamofire/5.4.1" \
    -H "Accept-Encoding: br;q=1.0, gzip;q=0.9, deflate;q=0.8" \
    -H "Accept-Language: en;q=1.0" \
    -H "Authorization: Token be1d6e220931d0249e3d600ef9682909f39252ec" \
    -H "Content-Type: application/x-www-form-urlencoded; charset=utf-8" \
    -d "confirmed=0&playerFour=8b52d186-ab60-438e-92f1-a3d35430ea81&playerOne=f33e09b5-be8b-49e7-99c2-1e2df7512a71&playerThree=583e0143-7352-4907-a806-a3b16baceefc&playerTwo=24da0653-7fbc-41c3-8a47-bdefb9323eb2&points%5B%5D%5Btype%5D=sk&points%5B%5D%5Bscorer%5D=f33e09b5-be8b-49e7-99c2-1e2df7512a71&points%5B%5D%5Btype%5D=tk&points%5B%5D%5Bscorer%5D=f33e09b5-be8b-49e7-99c2-1e2df7512a71&points%5B%5D%5Btype%5D=tk&points%5B%5D%5Bscorer%5D=f33e09b5-be8b-49e7-99c2-1e2df7512a71&points%5B%5D%5Bscorer%5D=f33e09b5-be8b-49e7-99c2-1e2df7512a71&points%5B%5D%5Btype%5D=tk&points%5B%5D%5Bscorer%5D=f33e09b5-be8b-49e7-99c2-1e2df7512a71&points%5B%5D%5Btype%5D=tk&team_one_score=11&team_two_score=0&time_ended=2021-09-11%2012%3A18%3A55&time_started=2021-09-11%2012%3A18%3A51" \
"http://127.0.0.1:8000/games/"

Larme figured it out. For some reason or another my alamofire request was encoding it using url encoding instead of json encoding. I just added "encoding: JSONEncoding.default" to my request params and everything works fine now.

Adam Berard
  • 45
  • 1
  • 7
  • Could you print `parameters`? `"confirmed": false` changed into `'confirmed': ['0'], ` is strange too... Also, how do you know that's how they are sent? Where did you check that? If you put `.cURLDescription { print("cURL: \($0)) }` between `request(...)` & `responseDeodable()`?, what's the output of the print? – Larme Sep 11 '21 at 19:02
  • Okay I just added both that info. I figured it was sending that because I print out the requests.data in my Django server right when it gets it. I figured the problem was on the Xcode side because like I said if I send the json with Postman it works fine. – Adam Berard Sep 11 '21 at 19:21
  • 1
    Header content type set to urlencoded that’s why... did you override the default value? – Larme Sep 11 '21 at 19:32
  • Not that I know of. I'm assuming it needs to be jsonencoded? – Adam Berard Sep 11 '21 at 19:39
  • @Larme You're a genius. I just added "encoding: JSONEncoding.default" to my request parameters and it works like I wanted it to. Thanks a million dude. This website is incredible. – Adam Berard Sep 11 '21 at 19:50

0 Answers0