Pass array in HTTP request body - swift

I want to pass array like this in my request:
{
"ids": ["5ed7603ab05efe0004286d19", "5ed7608ab05efe0004286d1a"]
}
I did like that but I am not sure the server is getting as I intended:
request.httpBody = try JSONSerialization.data(withJSONObject: ids, options: .prettyPrinted)

You should be using a Dictionary for this purpose. Here's how:
let dictionary = ["ids": ["5ed7603ab05efe0004286d19", "5ed7608ab05efe0004286d1a"]]
request.httpBody = try JSONSerialization.data(withJSONObject: dictionary, options: .prettyPrinted)
Suggestion: The modern approach would be to use JSONEncoder(). You could google this, there are plenty of solutions online. If you're still struggling you can ask for the method in the comments, I'll help you figure out.
Update: How you can implement Swift's JSONEncoder API in your code.
let dictionary = ["ids": ["5ed7603ab05efe0004286d19", "5ed7608ab05efe0004286d1a"]]
request.httpBody = try JSONEncoder().encode(dictionary)
Using a struct would be much safer. Here's how:
typealias ID = String
struct MyRequestModel: Codable { var ids: [ID] }
let myRequestModel = MyRequestModel(ids: ["5ed7603ab05efe0004286d19", "5ed7608ab05efe0004286d1a"])
request.httpBody = try JSONEncoder().encode(myRequestModel)
Note: Usage of type-alias is optional it just adds a bit of readability to your code, as is the usage of JSONDecoder.

You can encode using JSONEncoder;
let yourList = ["5ed7603ab05efe0004286d19", "5ed7608ab05efe0004286d1a"]
struct DataModel: Encodable {
var ids: [String]
}
let data = DataModel(ids: yourList)
let encodedData = try! JSONEncoder().encode(data)
// JSON string value
let jsonString = String(data: encodedData, encoding: .utf8)

Related

How to handle dynamic keys with single element as value in Swift

I'm having a problem figuring out how to handle dynamic keys with a single value one level deep. I've seen a few examples but they appear to handle other cases.
Here's an example.
[
{ "asdofjiodi": "asdofidj.com" },
{ "sadjlkj": "iejjol.com" },
{ "ijijwjljlijl": "adsijf.com" },
{ "jgncmkz": "mlkjaoijf.com" }
]
Any ideas on how I accomplish this with Codable or CodingKey? This is what I'd like for the end result.
["asdofidj.com", "iejjol.com", "adsijf.com", "mlkjaoijf.com"]
let url = Bundle.main.url(forResource: "file", withExtension: "json")!
let data = try! Data(contentsOf: url)
let decoder = JSONDecoder()
// NOTE: The below line doesn't work because I'm not sure how to do the encoding/decoding
try? decoder.decode([[String: String]].self, from: data)
First of all, is not a valid JSON. A "Valid JSON" can be a JSON Array (multiple json objects) or a single JSON object (starts and ends with { and })
After cleaning this...
You are trying to make a dictionary (json object) from a data. You don't need JSONDecoder to accomplish this. Try using JSONSerialization with jsonObject static function...
let data = Data("{\"asdofjiodi\": \"asdofidj.com\",\"sadjlkj\": \"iejjol.com\",\"ijijwjljlijl\": \"adsijf.com\",\"jgncmkz\": \"mlkjaoijf.com\"}".utf8)
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any]
let values = json?.map({ $1 })
print(values)
I Hope I helped!
JSONSerialization Apple documentation
Here is a minimal working example, given the JSON in your question:
let json = """
[
{ "asdofjiodi": "asdofidj.com" },
{ "sadjlkj": "iejjol.com" },
{ "ijijwjljlijl": "adsijf.com" },
{ "jgncmkz": "mlkjaoijf.com" }
]
"""
let data = Data(json.utf8)
let decoder = JSONDecoder()
do {
let decodedJson = try decoder.decode([[String: String]].self, from: data)
let values = decodedJson.compactMap(\.values.first)
print(values)
} catch {
print("Error: \(error.localizedDescription)")
}
If this doesn't appear to work for you, it may be related to how you load in the JSON.

Swift Encoding a JSON dictionary [String: Any], can't get rid of the quotes

We are grabbing some data from an API, which we decode into a [String: Any] dictionary.
We want to then take this dictionary, and mutate a value for a key "$twitter_card".
Then we want to encode that back into Data, which we send back to the API.
print(seeEditResponseString)
guard let data = seeEditResponseString.data(using: .utf8) else {return}
if let dict = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any], var dataDict = dict["data"] as? [String: Any] {
let summary: Any = "summary"
print(dataDict)
dataDict["$twitter_card"] = summary
print(dataDict)
let url = URL(string: "https://api2.branch.io/v1/url?url=\(link)")!
var request = URLRequest(url: url)
request.httpMethod = "PUT"
let jsonData = try? JSONSerialization.data(withJSONObject: ["branch_key": EnvConstants.branchKey, "branch_secret": EnvConstants.branchSecretKey, "data": dataDict])
request.httpBody = jsonData
Print statement before setting "$twitter_card":
["~feature": TwitterShare, "$publicly_indexable": 1, "$one_time_use": 0, "~id": 881331908143151724, "~creation_source": 3, "~channel": twitter, "$og_description": HELLO WORLD, "$twitter_app_country": US, "$twitter_card": summary_large_image]
After setting "$twitter_card":
["~feature": TwitterShare, "$publicly_indexable": 1, "$one_time_use": 0, "~id": 881331908143151724, "~creation_source": 3, "~channel": twitter, "$og_description": HELLO WORLD, "$twitter_app_country": US, "$twitter_card": "summary"]
Notice how before there aren't quotes in the value, but after there is. We believe the API request is failing because of this, but can't figure out how to match the same data type on the request back.
EDIT: here is the response as a String:
"{\"data\":{\"$og_title\":\"Locker Room\",\"$publicly_indexable\":true,\"$deeplink_path\":\"open?link_click_id=881284646317326515\",\"~creation_source\":3,\"$locally_indexable\":true,\"$og_description\":\"HELLO WORLD\",\"$uri_redirect_mode\":\"2\",\"$identity_id\":\"880941693229513892\",\"$twitter_card\":\"summary_large_image\",\"$og_image_url\":\"https://bettylabs.io/lockerroom_logo_play_wide.png\",\"room\":\"91939938-1171-4e61-9ecf-5934b92298cf\",\"~feature\":\"TwitterShare\",\"url\":\"https://dummylit.com/CScjyeLyidb\",\"$twitter_title\":\"Disco Dev\",\"$fallback_url\":\"https://dummylit.com/room/91939938-1171-4e61-9ecf-5934b92298cf\",\"+url\":\"https://dummylit.com/CScjyeLyidb\",\"$ios_deeplink_path\":\"open?link_click_id=881284646317326515\",\"$canonical_identifier\":\"room/91939938-1171-4e61-9ecf-5934b92298cf\",\"$twitter_app_country\":\"US\",\"inviterId\":\"a5e7f4b2-580a-4661-aad3-2c78bb4a78c7\",\"$one_time_use\":false,\"~id\":\"881743970732394436\",\"~channel\":\"twitter\",\"$twitter_description\":\"Green Sock\"},\"type\":0,\"feature\":\"TwitterShare\",\"channel\":\"twitter\"}"

Swfit URLRequest POST How to make a request with duplicated parameter keys

I'm currently working on a project which uses Inoreader APIs, and there is one required post request with duplicated parameter keys.
https://www.inoreader.com/reader/api/0/edit-tag?a=user/-/state/com.google/read&i=12345678&i=12345679
As you can see duplicated i parameters. I've tried below to set httpBody of URLRequest, failed silently.
var parameters = [String: Any]()
parameters["i"] = idArray
parameters["a"] = "user/-/state/com.google/read"
let jsonData = try JSONSerialization.data(withJSONObject: parameters, options: [])
request.httpBody = jsonData
// I omitted other codes for simplicity
Then I've tried to chain every element of idArray with "&i=", assigned it to parameters["i"], also failed silently.
// chainedID was something like 123&i=456&i=789, to make it looks like as url example given by documentation
parameters["i"] = chainedID
How can I do that? This API is working perfectly with one item, but I can't get it work on multiple items. It claims will work with multiple items.
Based on the example that you posted and the ones that the documentation mentions, although the request is POST can accept parameters in the URL's query.
So, the solution would be to compose your request using URLComponents:
var components = URLComponents(string: "https://www.inoreader.com/reader/api/0/edit-tag")
var queryItems = [
URLQueryItem(name: "a", value: "user/-/state/com.google/read")
]
idArray.forEach {
queryItems.append(URLQueryItem(name: "i", value: $0))
}
components?.queryItems = queryItems
guard let url = components?.url else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
and not use JSON parameters in the request's body.

HttpRequest with multiple parameters swift

I'm trying to create a request with multiple parameters using Swift. So far I managed to create with one parameter but not with multiple.
I tried to use a Dictionary but couldn't do it.
Here is my actual code:
let protocolo = txtProtocolo.text!
var request = URLRequest(url: url)
let parameters = "protocolo=\(protocolo) "
request.httpMethod = "POST"
request.httpBody = parameters.data(using: String.Encoding.utf8)
URLSession.shared.dataTask(with: request)
{ (data, response, error) in
....
I'm trying to do something like this:
let dictionary = ["protocolo":protocolo,
"secondParameter": "value"]
And use this dictionary as httpBody.
Thanks in advance for your help.
If you have the option use Alamofire. It is very good :)
But if you want to use the dictionary. It seems you have to convert it to a string. Did you try something like
let parameters = ["auth":"asdf", "width":"123"]
let parametersString = (parameters.compactMap({ (key, value) -> String in
return "\(key)=\(value)"
}) as Array).joined(separator: "&")
And use the parametersString as the parameter

Swift how to send symbols like "&(*(" with HTTP post

I have got this Swift code
var request = URLRequest(url: URL(string: "http://www.centill.com/ajax/logreg.php")!)
request.httpMethod = "POST"
let postString = "app_log_pass=\(pass_text_field.text!)"
request.httpBody = postString.data(using: .utf8)
And when my text is
(1&*^&&2
It only prints (1.How can i send string that contains various symbols safely with Swift?
As Sulthan suggested, no predefined CharacterSet can be used for actual servers when you want to include some symbol characters in your POST data.
An example CharacterSet which I often use:
extension CharacterSet {
static let rfc3986Unreserved = CharacterSet(charactersIn:
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~")
}
let postValue = "(1&*^&&2"
let postString = "app_log_pass=\(postValue.addingPercentEncoding(withAllowedCharacters: .rfc3986Unreserved)!)"
print(postString) //->app_log_pass=%281%26%2A%5E%26%262
Another CharacterSet which would work for usual PHP servers:
extension CharacterSet {
static let queryValueAllowed = CharacterSet.urlQueryAllowed.subtracting(CharacterSet(charactersIn: "&+="))
}
let postValue = "(1&*^&&2"
let postString = "app_log_pass=\(postValue.addingPercentEncoding(withAllowedCharacters: .queryValueAllowed)!)"
print(postString) //->app_log_pass=(1%26*%5E%26%262
Anyway, you need to escape the key and the value separately, to prevent escaping the separator =.
ADDITION
An example of escaping multiple key-value pairs:
extension String {
var queryValueEscaped: String {
return self.addingPercentEncoding(withAllowedCharacters: .queryValueAllowed)!
}
}
let params: [String: String] = [
"app_log_usn": "OOPer",//usn_text_field.text!,
"app_log_pass": "(1&*^&&2"//pass_text‌​_field.text!
]
let postString = params.map {"\($0.key)=\($0.value.queryValueEscaped)"}.joined(separator: "&")
print(postString) //->app_log_usn=OOPer&app_log_pass=(1%26*%5E%26%262
Assuming each key is made of safe characters and no need to escape.
Use the addingPercentEncoding method with the urlHostsAllowed parameter:
string.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)