I need to convert the following curl call of Microsoft Azure Cloud to Swift:
curl -X POST "https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&to=zh-Hans&toScript=Latn" -H "Ocp-Apim-Subscription-Key: <client-secret>" -H "Content-Type: application/json; charset=UTF-8" -d "[{'Text':'Hello, what is your name?'}]"
I need to convert a JSON call of curl to Swift. The problem is I have no experience with JSON calls and don't know how to include headers, a body and parameters. So I tried the following code:
let apiKey = "..."
let location = "..."
class AzureManager {
static let shared = AzureManager()
func makeRequest(json: [String: Any], completion: @escaping (String)->()) {
guard let url = URL(string: "https://api.cognitive.microsofttranslator.com/translate"),
let payload = try? JSONSerialization.data(withJSONObject: json) else {
return
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue(apiKey, forHTTPHeaderField: "Ocp-Apim-Subscription-Key")
request.addValue(location, forHTTPHeaderField: "Ocp-Apim-Subscription-Region")
request.addValue("application/json", forHTTPHeaderField: "Content-type")
request.addValue("NaiVVl3DEFG3jdE5DE1NFAA6EABC", forHTTPHeaderField: "X-ClientTraceId")
request.httpBody = payload
URLSession.shared.dataTask(with: request) { (data, response, error) in
guard error == nil else { completion("Error::\(String(describing: error?.localizedDescription))"); return }
guard let data = data else { completion("Error::Empty data"); return }
let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any]
if let json = json,
let choices = json["translations"] as? [String: Any],
let str = choices["text"] as? String {
completion (str)
} else {
completion("Error::nothing returned")
}
}.resume()
}
}
And then call it like this:
let jsonPayload = [
"api-version": "3.0",
"from": "en",
"to": "it",
"text": "Hello"
] as [String : Any]
AzureManager.shared.makeRequest(json: jsonPayload) { [weak self] (str) in
DispatchQueue.main.async {
print(str)
}
}
Well, the errors I get are:
Optional(["error": {
code = 400074;
message = "The body of the request is not valid JSON.";
}])
And:
Error::nothing returned
Here are the Docs of Microsoft Azure Translator: https://docs.microsoft.com/de-de/azure/cognitive-services/translator/reference/v3-0-translate
CodePudding user response:
Let's analyze your cURL command
, let's make it more readable with breaking line. If you want to make it working as such in Terminal.app, just add \
at the end of the line.
curl \
-X POST \
"https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&to=zh-Hans&toScript=Latn" \
-H "Ocp-Apim-Subscription-Key: <client-secret>" \
-H "Content-Type: application/json; charset=UTF-8" \
-d "[{'Text':'Hello, what is your name?'}]"
-H
: Set with addValue(_,forHTTPHeaderField:)
-d
: Set it with httpBody =
The long URL, that's request.url
There are two main differences between the cURL command and the URLRequest
you mode.
First, you did mix payload & URL settings.
url
should be "https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&to=zh-Hans&toScript=Latn"
, not "https://api.cognitive.microsofttranslator.com/translate"
, you put api-version
, to
, toScript
(that one is missing by the way) inside httpBody
.
Put them into the URL instead. You can use URLQueryItem
to format them correctly. See related question.
Now, once you fix that, there is a second issue:
[{'Text':'Hello, what is your name?'}]
, I'll by pass the strange fact that it's using single quotes, but, here JSON is an array at top level, not a dictionary. Also, it's Text
, not text
(it might be case sensitive).
If you want to see what you are sending in httpBody, do not hesitate to do:
if let jsonStr = String(data: request.httpBody ?? Data(), encoding: .utf8) {
print("Stringified httpBody: \(jsonStr)")
}
So fix your parameters until it matches the on in the working cURL
command. As said, it's an array, not a dictionary.
Finally, avoid using try?
, please do proper do
/try
/catch
because if it fails, at least you need to know about it.
No need to use .allowFragments
if you don't expect a JSON String at top level. Here you are expecting a Dictionary.
And I'd recommand to use Codable
now in Swift 4 instead of JSONSerialization
.
Edit: Seeing the doc, as you mixed the parameters/query, keep in mind:
"Query Parameters", that's for URLQueryItems
and it's in the URL.
Request Body: That's for the httpBody
.