How to post parameter with (+ plus sign) in Alamofire - swift

when post A+ or O+ or any (+) bloods types with + character, I receive a "not valid blood" error.
The blood value is in JSON dictionary: How do I post + character in Alamofire?
let dictionary = ["fname": "name",
"lname": "family",
"blood": "A+"]
let updateData = try! JSONEncoder().encode(dictionary)
let jsonString = String(data: updateData, encoding: .utf8)!
var components = URLComponents(string: registerUrl)!
components.queryItems = [
URLQueryItem(name: "api_key", value: apiKey),
URLQueryItem(name: "profile", value: jsonString)
]
Alamofire.request(components.url!, method: .post).responseJSON {
response in
if response.result.isSuccess {
let json: JSON = JSON(response.result.value!)
print(json)
}
}

Unfortunately, URLComponents will not percent encode + character although many (most?) web services require it to be (because, pursuant to the x-www-form-urlencoded spec, they replace + with space character). When I posted bug report on this, Apple's response was that this was by design, and that one should manually percent encode the + character:
var components = URLComponents(string: "https://www.wolframalpha.com/input/")!
components.queryItems = [
URLQueryItem(name: "i", value: "1+2")
]
components.percentEncodedQuery = components.percentEncodedQuery?.replacingOccurrences(of: "+", with: "%2B")
Obviously, if you were doing a standard application/json request, with the JSON in the body of the request, no such percent encoding is needed. But if you're going to include the JSON in the URL like this, then you will have to percent encode the + character yourself.
Alternatively, you can let Alamofire do this for you:
let parameters = [
"api_key": apiKey,
"profile": jsonString
]
Alamofire.request(url, method: .post, parameters: parameters).responseJSON { response in
...
}
This, admittedly puts the properly percent encoded value in the body of the POST request, not the URL as in your example, but generally in POST requests, that's what we want.

Related

Alamofire post request with params in the URL in swift

I have a post method with base url and various parameters appended in the base url itself.
The code is given below:
let todosEndpoint:String = "https://xxxxxxx/api/post_schedule_form_data?service_type=nanny&start_date=07/20/2020&start_time=06:00&end_date=07/20/2020&end_time=09:00&work_description=Work Description&special_instructions=Special Instructions&location_address=location_address&postal_code=abc123&current_profile_id=10"
let header: HTTPHeaders = ["Content-Type":"application/json","x-token":self.usertoken!]
print("the url is",todosEndpoint)
AF.request(todosEndpoint, method: .post, encoding: JSONEncoding.default, headers: header)
.responseJSON { response in
switch response.result {
case .success(let json):
print("Validation Successful",json)
case let .failure(error):
print(error)
}
}
I do not get any response in the code.What could the error be?
Your headers are not going through correctly...
Also, you're passing " " (spaces) in your URL, just replace them with "%20".
The correct way to do this would be:
let url:String = "https://xxxxxxx/api/post_schedule_form_data?service_type=nanny&start_date=07/20/2020&start_time=06:00&end_date=07/20/2020&end_time=09:00&work_description=Work Description&special_instructions=Special Instructions&location_address=location_address&postal_code=abc123&current_profile_id=10".replacingOccurrences(of: " ", with: "%20")
var request = URLRequest(url: NSURL(string: url)! as URL)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue(self.usertoken!, forHTTPHeaderField: "x-token")
AF.request(request).responseJSON { response in
switch response.result {
case .success(let json):
print("Validation Successful",json)
case let .failure(error):
print(error)
}
}
You are force unwrapping self.usertoken, so beware of that as well!
Also seems like the date in the URL has "/" which you must change to "%2F" in the URL or better the format in the backend!
Looks to me like you've got a number of illegal characters in your query params (e.g. ":", "/", " ").
I suggest you build your URL using URLComponents. Pass in the endpoint and the query params and then get back the path. That will show you what needs to be escaped.
Edit:
I guess I was wrong about ":" and "/". It looks like those are legal as part of a query param value.
This code:
var components = URLComponents()
components.scheme = "https"
components.host = "xxxxxxx"
components.path = "/api/post_schedule_form_data"
components.queryItems = [
URLQueryItem(name: "service_type", value: "nanny"),
URLQueryItem(name: "start_date", value: "07/20/2020"),
URLQueryItem(name: "start_time", value: "06:00"),
URLQueryItem(name: "end_date", value: "07/20/2020"),
URLQueryItem(name: "end_time", value: "09:00"),
URLQueryItem(name: "work_description", value: "Work Description"),
URLQueryItem(name: "special_instructions", value: "Special Instructions"),
URLQueryItem(name: "location_address", value: "location_address"),
URLQueryItem(name: "postal_code", value: "abc123"),
URLQueryItem(name: "current_profile_id", value: "10"),
]
print(components.string ?? "nil")
Outputs the string
https://xxxxxxx/api/post_schedule_form_data?service_type=nanny&start_date=07/20/2020&start_time=06:00&end_date=07/20/2020&end_time=09:00&work_description=Work%20Description&special_instructions=Special%20Instructions&location_address=location_address&postal_code=abc123&current_profile_id=10
Note that the spaces get escaped as %20.
That is probably the only illegal part of your URL string.
URLComponents is your friend because it applies escaping to the different parts of the URL as needed, and enforces the correct escaping rules for each of the different parts of the URL.
Edit #2:
This code:
let urlString: String = "https://xxxxxxx/api/post_schedule_form_data?service_type=nanny&start_date=07/20/2020&start_time=06:00&end_date=07/20/2020&end_time=09:00&work_description=Work Description&special_instructions=Special Instructions&location_address=location_address&postal_code=abc123&current_profile_id=10"
let componentsFromString = URLComponents(string: urlString)
print(componentsFromString?.string ?? "nil" )
displays "nil"
That tells you there is something illegal about your URL string.

Alamofire syntax for ecobee request

I'm trying to find the correct syntax for calling ecobee's API from Swift 4 using Alamofire.
Their cURL example:
curl -H "Content-Type: text/json" -H "Authorization: Bearer ACCESS_TOKEN" 'https://api.ecobee.com/1/thermostat?format=json&body=\{"selection":\{"selectionType":"registered","selectionMatch":"","includeRuntime":true\}\}'
The closest I've been to a solution is this
func doRequest() {
guard let url = URL(string: "https://api.ecobee.com/1/thermostat?format=json") else { return }
let parameters: Parameters = [
"selection": [
"selectionType": "registered",
"selectionMatch": ""
]
]
let headers: HTTPHeaders = [
"Content-Type": "text/json",
"Authorization": "Bearer \(core.accessToken)"
]
let req = AF.request(url, method: .get, parameters: parameters, encoding: JSONEncoding.default, headers: headers)
.responseJSON { response in
print("Error:", response.error?.localizedDescription ?? "no error")
print("Data:", String(data: response.data!, encoding: .utf8)!)
}
debugPrint(req)
}
When I run this, the call ultimately fails with status code 408, a server timeout.
When I change the HTTP method to use .post, the call completes, but the response is an internal status 3 with message "Update failed due to a communication error."
Can anyone help me figure out what I'm doing wrong before I waste another day trying to hack my way through it?
Ecobee's request format is a bit bizarre, as it uses form encoded parameters, but one of the values is a JSON encoded body. You'll have to do a little bit of prep work, as Alamofire doesn't naturally support something like this. This is just sample code, you'll need to do the work to make it safer.
First, encode the JSON parameters and get the String value:
let jsonParameters = ["selection": ["selectionType": "registered", "selectionMatch": ""]]
let jsonData = try! JSONEncoder().encode(jsonParameters)
let jsonString = String(decoding: jsonData, as: UTF8.self)
Then, create the actual parameters and headers values:
let parameters = ["format": "json", "body": jsonString]
let token = "token"
let headers: HTTPHeaders = [.authorization(bearerToken: token), .contentType("text/json")]
let url = URL(string: "https://api.ecobee.com/1/thermostat")!
And make the request:
AF.request(url, parameters: parameters, headers: headers).responseJSON { response in ... }

Request error Google Cloud NLP API with Swift

I am trying to make a request to Google Cloud NLP API to obtain sentiment analysis for a piece of text. I used Postman to design the correct request, and I was able to get a valid response using Postman. However, when I try to make the same request from Swift, it gives me an error. The error and code snippet used to make the request is shown below.
func sendAPIRequest(with text: String){
print("Text: ", text)
let jsonRequest = [
[
"document":[
"type":"PLAIN_TEXT",
"language": "EN",
"content":"'Lawrence of Arabia' is a highly rated film biography about British Lieutenant T. E. Lawrence. Peter O'Toole plays Lawrence in the film."
],
"encodingType":"UTF8"
]
]
let jsonObject = JSON(jsonRequest)
let headers: HTTPHeaders = [
"X-Ios-Bundle-Identifier": "\(Bundle.main.bundleIdentifier ?? "") ",
"Content-Type": "application/json"
]
let APIRequest = Alamofire.request("https://language.googleapis.com/v1/documents:analyzeSentiment?key=\(gCloudAPIKey)", method: .post , parameters: jsonRequest as? [String: Any], encoding: JSONEncoding.default , headers: headers).responseJSON { (response) in
print(response)
if let json = response.result.value {
print("JSON: \(json)")
}
}
Error:
JSON: {
error = {
code = 400;
details = (
{
"#type" = "type.googleapis.com/google.rpc.BadRequest";
fieldViolations = (
{
description = "Must have some text content to annotate.";
field = "document.content";
}
);
}
);
message = "One of content, or gcs_content_uri must be set.";
status = "INVALID_ARGUMENT";
};
}
Sorry. Solved it. My jsonRequest should be of type Parameters according to Alamofire.
let jsonRequest: Parameters =
[
"document":[
"type":"PLAIN_TEXT",
"language": "EN",
"content":"\(text)"
],
"encodingType":"UTF8"
]

Json key/value incorrectly formatted

Trying to login to a web service as such:
let parameters: Parameters = [
"Password":password,
"Username":username
]
var headers:HTTPHeaders = commonHeaders()
headers["Content-Type"] = "application/json;charset=UTF-8"
Alamofire.request(url!, method:.post, parameters:parameters, headers:headers).responseJSON { response in
....
}
Somehow, my parameters end up on the server side as
"Username=xxx&Password=yyy"
where
{"Username":"xxx","Password":"yyy"}
is expected
This must be something simple, but I hope you can help me
Add the type of encoding you want your parameters to be in your request, Default is .URLEncoding. So, your parameters are going like "Username=xxx&Password=yyy"(appended into url)
Add the parameter json encoding to your request:
Alamofire.request(url!, method:.post, parameters:parameters, headers:headers, encoding: JSONEncoding.default)

Set headers with nested key-value in Alamofire

I'm trying to set the headers of an Alamofire 4 request where the headers have a key whose value is another key-value pair. The swift compiler is happy with my header variables but Alamofire won't take them.
This is what I have:
let subHeader = [
"some-key": "some-value",
"another-oey": "another-value"
]
let headers : [String: Any] = [
"some-header-key": "some-header-value",
"subdata": [subHeader]
]
// Upload data
Alamofire.upload(data,
to: uploadurl,
method: .put,
headers: headers)
.validate()
.response { response in
// Handle response
}
If you have a look at Alamofire's source, you can see HTTPHeaders is actually [String: String]. Basically, Alamofire expects [String: String] from you as headers.
What you can do is converting your subheader into String with NSJSONSerialization and pass it to Alamofire.
let dictData = try! JSONSerialization.data(withJSONObject: subheader)
let dictString = String(data: dictData, encoding: String.Encoding.utf8)!
print(dictString)
// Now use "dictString" instead of "subheader" in your "headers" dictionary
You can do error handling however you want, I used !s for simplicity.