(Swift) twilio post request to set attributes using alamofire - json

I am attempting to use Twilio's REST Api and Alamofire to set certain Attributes to a Channel when creating it (https://www.twilio.com/docs/api/ip-messaging/rest/channels#action-create)
let parameters : [String : AnyObject] = [
"FriendlyName": "foo",
"UniqueName": "bar",
"Attributes": [
"foo": "bar",
"bar": "foo"
],
"Type": "private"
]
Alamofire.request(.POST, "https://ip-messaging.twilio.com/v1/Services/\(instanceSID)/Channels", parameters: parameters)
.authenticate(user: user, password: password)
.responseJSON { response in
let response = String(response.result.value)
print(response)
}
Using that code, the response I received back was that a Channel was created with FriendlyName foo and UniqueName bar, but that Channel had no Attributes set.
Looking at the Alamofire github (https://github.com/Alamofire/Alamofire) I see that there's a way to send a POST Request with JSON-encoded Parameters. So I tried this:
let parameters : [String : AnyObject] = [
"FriendlyName": "foo",
"UniqueName": "bar15",
"Attributes": [
"foo": "bar",
"bar": "foo"
],
"Type": "private"
]
Alamofire.request(.POST, "https://ip-messaging.twilio.com/v1/Services/\(instanceSID)/Channels", parameters: parameters, encoding: .JSON)
.authenticate(user: user, password: password)
.responseJSON { response in
let response = String(response.result.value)
print(response)
}
When adding "encoding: .JSON" to the request the response shows that not only were the Attributes not set but also the FriendlyName and UniqueName were nil, unlike before when they were correctly set using the URL-Encoded Parameters.
Am I setting the Attributes wrong in 'parameters'? Twilio's documentation says that Attributes is "An optional metadata field you can use to store any data you wish. No processing or validation is done on this field."
Help would be appreciated :)

I have figured out an answer to my question. Turns out I had formatted the Attributes field incorrectly.
Here is the code that worked for me:
let parameters : [String : AnyObject] = [
"FriendlyName": "foo",
"UniqueName": "bar",
"Attributes": "{\"key\":\"value\",\"foo\":\"bar\"}",
"Type": "private"
]
Alamofire.request(.POST, "https://ip-messaging.twilio.com/v1/Services/\(instanceSID)/Channels/", parameters: parameters)
.authenticate(user: user, password: password)
.responseJSON { response in
let response = String(response.result.value)
print(response)
debugPrint(response)
}
Hope this helps others :)

Related

Swift JSONEncoding problem with encoding a list of dictionaries

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.

Swift : How to create a dictionary dynamically from a json?

I'd like some advice from you. I would like to create a dictionary from a dynamic response fetch from an API and send that dictionary in an Alamofire POST request.
From what I have so far it's working but I'm not satisfied with what i've made and I think the code is really messy.
Here is an example of what I can receive
"content": {
"type": "form",
"fields": [
{
"type": "select",
"label": "Do you have your documents?",
"field": "user.has_docs",
"default": 0,
"choices": [
{
"value": 0,
"name": "Not yet"
},
{
"value": 1,
"name": "I do"
}
]
},
{
"type": "input",
"field": "user.date",
"label": "When do you arrive?",
}
]
}
After parsing the json with the Codable protocol, I have all my data in the Model Field
type: String
label: String
field: String
defaultValue: Int?
choice: [Choice]?
Choice
value: Int
name: String
So I want to create my dictionary and I want the following scheme :
{
"value": {
"user": {
"has_docs": 1,
"date": "29/07/2020"
}
}
}
The key named : "value" is always the same value, but the other one depends of the result from the API. the prefix of the field corresponding of "parent object" and the right part is the child.
Hard coding a dictionary in Swift is not that hard, I would do
let dict = [
"value": [
"user": [
"has_docs": 1,
"date": "29/07/2020"
]
]
]
But the troubles begin, at the attempt of creating a dictionary dynamically. Values inside user keep only the last one and replacing has_docs with date.
I have found a workaround with using flatmap and reduce but it only allows the type [String: String], unfortunately I need to write [String: Int] too in the dictionary.
here is a sample of the code
let flattenedDictionary = [key : dictionaries
.flatMap { $0 }
.reduce([String:String]()) { (dict, tuple) in
var nextDict = dict
nextDict.updateValue(tuple.1 as! String, forKey: tuple.0)
return nextDict
}]
parameters["value"] = flattenedDictionary
Here :
key = "user".
dictionaries = [["has_docs": 1], ["date": "29/07/2020"]]
Feel free to exchange if you need more informations
If you have any clue on how you could helping me, I'll highly appreciate, thanks for reading so far.
I hope I was very understandable.
Edit
From a general view : I'd like to create a dictionary dynamically
[String: [String: [String: Any]]]
A bit unclear if you have a [String: [String: [String: Any]]] or [String: [String: Any]] dictionary, but the concept of creating it dynamically would be rather similar.
var user: [String: Any] = [:]
user["has_docs"] = 1
user["date"] = Date()
let dict = ["value": user]

How to make Swift JSON request params

I got a problem with making a request to a server, I got a parsing error, could you help me to make the request properly?
I need to make a request like this:
"{ \"jsonrpc\": \"2.0\", \"id\": 1, \"method\": \"call\", \"params\": [ \"c36c5a835cf88e82f97dcfa5b74f53f4\",\"network.interface.wan\",\"status\", {} ] }"
My request:
["jsonrpc": "2.0", "id": 1, "method": "call", "params": [token, "network.interface.wan", "status", []]]
If you want to convert Dictionary as JSON String response then you can try like this way.
Edit: In JSON string with params array the last object is empty dictionary so you need to set last object of params array as [:] instead of []
let dic:[String:Any] = [
"jsonrpc": "2.0",
"id": 1,
"method": "call",
"params": [
"token",
"network.interface.wan",
"status", [:]
]
]
if let data = try? JSONSerialization.data(withJSONObject: dic),
let string = String(data: data, encoding: .utf8){
print(string)
}
Output
{\"method\":\"call\",\"jsonrpc\":\"2.0\",\"id\":1,\"params\":[\"token\",\"network.interface.wan\",\"status\",{}]}

Alamofire POST Request

I was hoping someone could explain what the POST request (with parameters) accomplishes. When I run the following command, provided by Alamofire's documentation, this is what is produced:
Code
let url = "https://httpbin.org/post"
let parameters: Parameters = [
"foo": "bar",
"baz": ["a", 1],
"qux": [
"x": 1,
"y": 2,
"z": 3
]
]
Alamofire.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default).responseJSON { response in
if((response.result.value) != nil) {
let jsonVar: JSON = JSON(response.result.value!)
print(jsonVar)
}
}
Result
{
"files" : {
},
"origin" : "192.104.181.247",
"data" : "{\"baz\":[\"a\",1],\"qux\" {\"y\":2,\"x\":1,\"z\":3},\"foo\":\"bar\"}",
"headers" : {
"Accept" : "*\/*",
"Accept-Encoding" : "gzip;q=1.0, compress;q=0.5",
"Content-Type" : "application\/json",
"Host" : "httpbin.org",
"Content-Length" : "53",
"User-Agent" : "HTTPRequest\/1.0 (com.dexstrum.HTTPRequest; build:1; iOS 10.2.0) Alamofire\/4.3.0",
"Accept-Language" : "en;q=1.0"
},
"json" : {
"baz" : [
"a",
1
],
"foo" : "bar",
"qux" : {
"x" : 1,
"y" : 2,
"z" : 3
}
},
"form" : {
},
"args" : {
},
"url" : "https:\/\/httpbin.org\/post"
}
What exactly is occurring with the parameters and the POST request?
The url you are hitting it allows a post request. You can get json response by get or post method. Usually you fetch data by get method and send data by post method. When sending data with post request it needs some parameter to satisfy its requirement , usually its predefined.
SO you are sending a post request here with alamofire and along with some parameter which satisfy its requirements. Here you are encoding the parameter as JSONEncoding.default which means it sends the parameter data encoded as json finally you get the response as json coz you declared .responseJSON . Hope that helps.

Adding [[String:AnyObject]] as parameter on Alamofire call (Swift 2)

I have this JSON (this is a simplified version):
{
"from":0,
"location": [{
"city":"Barcelona"
}]
}
And i'm trying to do an Alamofire request with this JSON on parameters. So I have typed something like this:
let parameter = [
"from":0,
"location": [
["city": "Barcelona"]
]
]
But it seems Alamofire doesn't accept the parse from [{ }] to [[]] on parameters. I have read that one way to solve this is using a MutableURLRequest but i don't find any clear example.
Alamofire.request(.GET, "http://myurl", parameters: (params as! [String : AnyObject]), headers: nil).responseCollection { (response: Response<[Group], NSError>) in
switch response.result {
case .Success(let groups):
print("Success")
case .Failure(_):
print("Failure")
}
}
This code above works and I get a result from this Alamofire Request but ignores this:
"location": [ ["city": "Barcelona"]]
Could someone can help with that? Thank you so much.