I would like to submit translation requests to the Watson Translation service from Swift but don't want to use the Swift SDK. (IBM plans to discontinue Pod and Carthage support of the SDK due to many issues it says it cannot resolve but beyond that a full blown SKDK seems like overkill). In the past, I've accessed other Watson services with a post request. However, I can't find any examples on using the translation service without the SDK in the documentation.
There are, however, curl examples so if you can call the API from a terminal window it ought to be possible to call it with some Swift code, however, I am not sufficiently knowledgeable about curl to convert this into Swift calls.
Can anyone suggest how to do this with either a get or post https call or point me in the right direction?
Here is curl from the IBM translation documentation:
curl -X POST --user "apikey:{apikey}" --header "Content-Type: application/json" --data '{"text": ["Hello, world.", "How are you?"], "model_id":"en-es"}' "{url}/v3/translate?version=2018-05-01"
The url seems to refer to a service endpoint such as:
https://api.us-south.language-translator.watson.cloud.ibm.com/v3/translate?version=2018-05-01 and the API key is the one given to the user.
Edit:
Putting the curl into reqbin and selecting Raw gives:
POST /instances/~/v3/translate?version=2018-05-01 HTTP/1.1
Authorization: Basic xxx-xxx-xxx-xxx-xxx
Host: api.us-east.language-translator.watson.cloud.ibm.com
Content-Type: application/json
Content-Length: 63
{"text": ["Hello, world.", "How are you?"], "model_id":"en-es"}
My problem is how to convert that to a Post request that is what is in the header and so forth.
Here is some boilerplate code for a post call that I am trying to adapt. It compiles and runs in Swift, however, the API is not allowing authorization (returns 401 Error) as the request is not properly formed.
Thanks for any suggestions on how to convert the CURL into a Post request.
Method with boilerplate code:
func postPhraseToTranslate (message:String,completion:#escaping (_ response:MyTranslation
let watsonurl: String = "https://api.us-south.language-translator.watson.cloud.ibm.com/v3/translate?version=2018-05-01 "
let parameters = ["text":message]
guard let parametersJson = try? JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted) else {//open 2
//print("")
return
}
let username = "xxx-xxx"
let password = "xxx-xxx-xxx-xxx-xxx-xxx-xxx"
let authString = username + ":" + password
guard let authData = authString.data(using: .ascii) else {//open 2
return
}//close 2
let authValue = "Basic " +
authData.base64EncodedString(options: Data.Base64EncodingOptions(rawValue: 0))
let url = URL(string: watsonurl)!
var request = URLRequest(url: url)
request.setValue( authValue, forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.httpBody = parametersJson
let task = URLSession.shared.dataTask(with: request) { //open 2
data, response, error in
guard let data = data, error == nil else {
// print("network error=\(error)")
return
}
print("got back the translation")
task.resume()
}
Thanks for any suggestions on how to convert the CURL to a Swift Get or Post call:
Related
I have created a sample app in Django which deletes a question from App. And provides a correct output when consumed using POSTMAN.
class Questions(APIView):
def delete(self,request):
received_id = request.POST["id"]
print(received_id)
place = Question.objects.get(pk=received_id)
place.delete()
questions = Question.objects.all()
seriliazer = QuestionSerializer(questions,many = True)
return Response({'Orgs': seriliazer.data})
However, when I am trying to achieve it from iOS app, it's returning {"detail":"Unsupported media type "text/plain" in request."}
func deleteQuestion( id: Int){
guard let url = URL(string: "http://127.0.0.1:8000/V1/API/questions/") else {
return
}
var request = URLRequest(url: url)
let postString = "id=15"
request.httpBody = postString.data(using: String.Encoding.utf8);
request.httpMethod = "DELETE"
URLSession.shared.dataTask(with: request) { data, response, error in
let str = String(decoding: data!, as: UTF8.self)
print(str)
if error == nil {
self.fetcOrganizatinData()
}
}.resume()
}
Could not really understand where exactly the problem is ?
If the api is expecting Json, the body you are sending is not Json, it’s encoded plain text. If it should be Json you can change the body string into the Json format like:
“{\”id\”:15}”
// you may want to tell it what you’re sending
request.setValue("application/json", forHTTPHeaderField: "Accept-Encoding")
Another thing it could be is the request is missing the Accept-Encoding header which tells the api what you’re sending up where Content-Type is what the api typically sends down.
I’ve experienced header injection when I’ve sent requests through specific gateways that aren’t always right. I’d the header isn’t present, something along the way could try to help you out and add the header. This has caused me problems on the past. I still don’t know exactly where in our stack it was occurring, but adding the header fixed my problem.
You can add the header like:
request.setValue("charset=utf-8", forHTTPHeaderField: "Accept-Encoding")
DELETE request's body will be ignored, I could guess from the Is an entity body allowed for an HTTP DELETE request? post. HENCE Better to send the complete URL or in header itself,
so I made the function as below
def delete(self,request):
received_id = request.headers['id']
place = Question.objects.get(pk=received_id)
place.delete()
return HttpResponse("DELETE view is working fine ")
and swift
func deleteQuestion( id: Int){
guard let url = URL(string: "http://127.0.0.1:8000/V1/API/questions/") else {
return
}
var request = URLRequest(url: url)
//let postString = "id=\(id)"
// request.httpBody = postString.data(using: String.Encoding.utf8);
request.httpMethod = "DELETE"
request.setValue("charset=utf-8", forHTTPHeaderField: "Accept-Encoding")
request.setValue("charset=utf-8", forHTTPHeaderField: "Content-Type")
request.setValue("\(id)", forHTTPHeaderField: "id")
URLSession.shared.dataTask(with: request) { data, response, error in
let str = String(decoding: data!, as: UTF8.self)
print(str)
if error == nil {
self.fetcOrganizatinData()
}
}.resume()
}
Shortly add Content-Type application/json in your headers
Reason
this happens because the postman has some default headers usually 8.
One of them is
Content-Type text/plain
and by writing "Content-Type": "application/json" we can overwrite that rule.
So whenever you want to pass your data like JSON do that.
to learn more what is by default in postman
I recommend you to read this official documentation of postman.
It happens with me I solved this with overwriting default Content-Type
I am trying to use rapidAPI to access the Yahoo Finance API for a personal project. I registered for a free account and got an API key. Rapid API allows their users to copy and paste code to request data, so I took their demo code for accessing the API. Here it is for swift when trying to access details about a stock:
import Foundation
let headers = [
"x-rapidapi-host": "apidojo-yahoo-finance-v1.p.rapidapi.com",
"x-rapidapi-key": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" //private key
]
let request = NSMutableURLRequest(url: NSURL(string: "https://apidojo-yahoo-finance-
v1.p.rapidapi.com/stock/get-detail?region=US&lang=en&symbol=APPL")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse)
}
})
dataTask.resume()
When I run this code, it gives me error 403. This means that this data is forbidden according to their website. I have tried using other APIs and copying and pasting the demo code and they work fine. This API shouldn't be depreciated because it is running on the website demo. Here is the returning message I was given in Xcode:
I just checked Yahoo Finance API. The GET /get-detail endpoint seems to be deprecated now. Despite the fact that it's deprecated, it's still working for me.
Make sure to subscribe to this API. this could be the cause of the 403 code
Im currently trying to work with the pho.to API in my iOS application. I am experimenting with making simple requests according to the documentation, however I cannot seem to get the request to go through successfully. Inside my API client file, I have this code:
let dataStr = """
<image_process_call>
<image_url>http://developers.pho.to/img/girl.jpg</image_url>
<methods_list>
<method order="1">
<name>desaturation</name>
</method>
<method order="2">
<name>caricature</name>
<params>type=1;crop_portrait=true</params>
</method>
</methods_list>
<thumb1_size>100</thumb1_size>
</image_process_call>
"""
let encodedStr = dataStr.replacingOccurrences(of: "\n", with: "").replacingOccurrences(of: " ", with: "")
let signData = encodedStr.hmac(key: key)
let urlStr = "https://opeapi.ws.pho.to/addtask/?app_id=\(appId)&key=\(key)&sign_data=\(signData)&data=\(encodedStr.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!))"
The HMAC encoding is being done according to this Stack Overflow post. Unfortunately when making a request to this URL using URLSession I get this response:
<?xml version=\"1.0\"?>\n<image_process_response><status>SecurityError</status><err_code>614</err_code><description>Error in POST parameters: one or more parameters (DATA , SIGN_DATA or APP_ID) are empty</description></image_process_response>
I feel like my issue is more related to actually forming the request rather than something specific to the API itself. I know my code is a little messy, however I was hoping that somebody could point me in the right direction in terms of making a request like this. Thanks!
As per their documentation you can see that data sent over from POST requests are in body (In cURL calls -d specifies the body of the request)
You are sending params/data in query, which the pho.to API doesn't accept, hence the error.
Here's a sample on how you can do:
let defaultSessionConfiguration = URLSessionConfiguration.default
let defaultSession = URLSession(configuration: defaultSessionConfiguration)
// Setup the request with URL
let url = URL(string: "https://opeapi.ws.pho.to/addtask")!
var urlRequest = URLRequest(url: url)
// Convert POST string parameters to data using UTF8 Encoding
let postData = yourXMLString.data(using: .utf8)
// Set the httpMethod and assign httpBody
urlRequest.httpMethod = "POST"
urlRequest.httpBody = postData
// Create dataTask
let dataTask = defaultSession.dataTask(with: urlRequest) { (data, response, error) in
// Handle your response here
}
// Fire the request
dataTask.resume()
I tried to make a Request with JWT Authorization, The server is Using Python/Flask-Restful. The API Works on Postman, so I guess there must be something wrong with my IOS Code. The server returns an error shows that
"Authorization Required. Request does not contain an access token",
I`m making the request from IOS Using following code.
func GetUserData(username: String, accesstoken: String,completion: #escaping (_ result: UserDataModel) -> Void){
let url = URL(string: "http://********/****/\(****)")
var request = URLRequest(url: url!)
request.httpMethod = "GET"
request.addValue("Authorization", forHTTPHeaderField: accesstoken)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let response = response as? HTTPURLResponse{
if response.statusCode != 200 {
print("Server Error When Update User Data")
} else {
if let data = data {
do {
******
completion(Data)
}
catch {
print(error)
}
}
}
}
}.resume()
}
I have no idea What is going on, Any help?
It looks like you're adding the header:
Bearer base64junk: Authorization
When instead you want:
Authorization: Bearer base64junk
You just have the parameters to addValue(_:forHTTPHeaderField:) backwards. You want this instead:
request.addValue(accesstoken, forHTTPHeaderField: "Authorization")
This should be obvious if you read that line of code like an English sentence ("value authorization for header field access token"?). In the future, you could also use something like Charles Web proxy to intercept your requests and verify that they are indeed formed the way you expect.
I am trying to login (form) into a website and parse data with Swift and NSURLSession.
I have been using cURL to confirm that this is actually possible, and the following code returns the webpage-body as expected.
curl -c cookie.txt -d "username=karl" -d "password=ivar" https://talkmore.no/talkmore3/servlet/Login
curl -b cookie.txt https://talkmore.no/talkmore3/servlet/SubscriptionUsage
When I am trying to do this in Swift, I fetch the Cookie from the response header, then I try to retrieve the SubscriptionUsage-webpage by passing the cookie along. However I cannot get this to work.
How can I convert this simple curl syntax to Swift-code using NSURLSession?
Edit: What I've got so far.
func login() -> Void {
var session = NSURLSession.sharedSession()
var request = NSMutableURLRequest(URL: NSURL(string: "https://www.talkmore.no/talkmore3/servlet/Login")!)
request.HTTPMethod = "POST"
var params = "username=karl&password=ivar"
request.HTTPBody = params.dataUsingEncoding(NSUTF8StringEncoding)
request.addValue("text/html", forHTTPHeaderField: "Content-Type")
var task = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) -> Void in
println("Response: \(response)")
println("Data: \(NSString(data: data, encoding: NSUTF8StringEncoding)))")
let httpResponse = response as NSHTTPURLResponse
var cookie = httpResponse.allHeaderFields["Set-Cookie"] as String
})
task.resume()
All seems good, I receive the response with a header including "Set-Cookie" = "JSESSIONID=D8307BA498EFE061C86636C51B8AC25D; Path=/talkmore3/; Secure; HttpOnly"; And I believe I have to send this cookie with the next request. But I have failed to replicate the cURL behavior.