I would like to use the Firestore REST API from Swift, because I am using Siri Shortcut Intents, where I am not able to use the native SDK.
What I have tried so far is to create an URLSession with "POST" httpmethod, but no luck. I have been able successfully to create document to use the form found on firestore website. But I could make successful Swift version of it.
Here is the code I have tried:
private func addTask() {
let parent = "projects/reality-kanban/databases/(default)/documents/l3VXrtTLoz11VGn60ott"
let collectionId = "A33XrtfL2ea3dG340era"
let urlString = "https://firestore.googleapis.com/v1/\(parent)/\(collectionId)"
let requestBody = DocumentBody(name: parent, fields: RequestTask(description: "test")) // it is a codable struct
let jsonData = try! JSONEncoder().encode(requestBody)
print(String(data: jsonData, encoding: .utf8)!)
let url = URL(string: urlString)!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = jsonData
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else {
print("Invalid Response received from the server") // this is what I get
return
}
}
task.resume()
}
This is the error I get: Invalid Response received from the server (400)
Add --debug to the command you are running to corroborate if you have set the right project.
I develop an iOS app using Swift.
I use the following method below to access an login ENDPOINT with PUT method.
let loginData = LoginModel("myUser","myPassword")
var loginClassJson:Data?
do{
loginClassJson = try JSONEncoder().encode(loginData)
} catch {
fatalError("Unable To Convert in Json")
}
let completeUrl = URL(string: RESconstantes.URL_PRINCIPAL_TREINAGEDAVE + "/login" )!
var request = URLRequest(url: completeUrl)
let myConfig = URLSessionConfiguration.default
let base64LoginString = EndpointController.getBase64StringLoginWithUserAndPasswordV2()
myConfig.httpAdditionalHeaders = ["Authorization" : base64LoginString]
request.httpMethod = "PUT"
request.setValue("\(String(describing: loginClassJson!.count))", forHTTPHeaderField: "Content-Length")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = loginClassJson
let sessionDelegate = SessionDelegate()
let urlSession = URLSession(configuration: myConfig, delegate: sessionDelegate, delegateQueue: OperationQueue.main)
let task = urlSession.dataTask(with: request as URLRequest, completionHandler: {
(data, response, error) in
if let error = error{
print("errorX: ")
print(error)
return
}
if let data = data{
let returnData = String(data: data, encoding: String.Encoding.ascii)
print("dataX: ")
print(returnData)
}
if let response = response{
print("responseX: ")
print(response)
}
})
task.resume()
print("END")
This is my URLSessionDelegate class
class SessionDelegate:NSObject, URLSessionDelegate
{
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
// usado para fazer o bypass na autenticação self-signed do certificado do servidor
// We've got a URLAuthenticationChallenge - we simply trust the HTTPS server and we proceed
print("start didReceive challenge 1")
if true {
print("didReceive challenge 2")
completionHandler(.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!))
}else{
completionHandler(.performDefaultHandling, nil)
}
}
It works perfectly for me, but now I try to create a code to access another ENDPOINT with POST method
let resDadoModel = ResDadoModel.getResenhaById(1)
let jsonRequestUploadResenha = ResDadoModel.createMockJsonObjectResenhaDados(resDadoModel)
let json: [String: Any] = jsonRequestUploadResenha
guard let jsonData:Data = try? JSONSerialization.data(withJSONObject: json) else {
print("guard jsonData error")
return
}
let completeUrl = URL(string: RESconstantes.URL_PRINCIPAL_TREINAGEDAVE + "/validaResenha" )!
var request = URLRequest(url: completeUrl)
let myConfig = URLSessionConfiguration.default
let base64LoginString = EndpointController.getBase64StringLoginWithUserAndPasswordV2()
myConfig.httpAdditionalHeaders = ["Authorization" : base64LoginString, "Content-Type":""]
request.httpMethod = "POST"
request.setValue("\(String(describing: jsonData.count))", forHTTPHeaderField: "Content-Length")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = jsonData
let sessionDelegate = SessionDelegate()
let urlSession = URLSession(configuration: myConfig, delegate: sessionDelegate, delegateQueue: OperationQueue.main)
let task = urlSession.dataTask(with: request as URLRequest, completionHandler: {
(data, response, error) in
if let error = error{
print("errorX: ")
print(error)
return
}
if let data = data{
let returnData = String(data: data, encoding: String.Encoding.ascii)
print("dataX: ")
print(returnData)
}
if let response = response{
print("responseX: ")
print(response)
}
})
task.resume()
print("END")
But the code that I use to access the ENDPOINT called "validaResenha" is not working properly, I get a 405 method not allowed error.
I get the following response data
<NSHTTPURLResponse: 0x600002028560> { URL: https://my_url_endpoint/api/spservicos/v1/validaResenha } { Status Code: 405, Headers {
Allow = (
"POST, OPTIONS"
);
"Cache-Control" = (
"no-cache=\"set-cookie, set-cookie2\""
);
Connection = (
"Keep-Alive"
);
"Content-Language" = (
"en-US"
);
"Content-Length" = (
0
);
"Content-Type" = (
"text/plain"
);
Date = (
"Thu, 23 Dec 2021 23:16:21 GMT"
);
Expires = (
"Thu, 01 Dec 1994 16:00:00 GMT"
);
"Keep-Alive" = (
"timeout=10, max=100"
);
"Set-Cookie" = (
"LtpaToken2=L6DEf/sqCSjiI1rePW3wEWZo40oNAsxmNVBNTpIRm3FZZRSSgaqmUTDYdjTq2PNE4+FhiIOKw7Xzuta4+LpD3cUB8QKZQ/KVom/rFFQ50XNkpQezmgMlgsmDDgtodRxVU5eyo1P1NP6r/3M55eY4HkeD583kXQB3/+EH3dIryo0ii6Jn6PrxaspX5noEo0eSt+yF2AylLdU66fCcSMJw7LCrB8Tulna4xHe4Nb9i+O5z2mnTXoIgbozDGuXfS6Y20zPrsaN62Bx1X/nySf1luf1QMhrt6P4SPF6GVudm0s/Db9dS0b444kJA4kMSJ0NbZ2khMzV1zSg3eZY6xZg2kidV8Qczpe5bL2/DNrPQY/CrUo8wcdFE1ebfxDcVrjv3G+nH6uKOPWtbcHHx9Wp1gvHLxj3cJ5MP43AzxW/7GXPA7QlsmlquxW1Ck7OypsP2hrYCvCWubjGdM51cg8uqhIonI+uXRO6BlcXIsPOfpR+LbQfDNo+9vzXzB+CZKZmYnBX63ffWhX09Cr+Ua0a2Sw8mOcE5jXImlO49+ak0FHPkiiaSABzuOl6ALYg9J6LCxjm6MC9bKd7KbMPueJI/ugVeMyphQwss5AHxic8fVmo+7/XNRT6zr4I/01N8xFQsqrvx5+i2AhxWO1bdDKmpZQLPoTHMD7TPcFBkwDXLVqXPXkpkcGvg3mI8ssKOOlxwJT7/SETcqrCY5O8Yr505qdeZiNIj4kjKiLoLuNpE+ZI=; Path=/"
);
} }
Anyone has an idea why I was receiving a 405 error method not allowed? The POST method works for me if I use POSTMAN. It works if I use PUT or GET endpoints with Swift code, but it fails if I try to use endpoints with POST method in Swift.
I see something strange, the "Content-Type" is defined as text/plain in the response, but I set it as "application/json". I don't understand why the config was not being set.
If I call it via POSTMAN, it works, but for some reason I don't know why it not works when I use Swift.
--- EDIT ---
After #matt suggestion, I use Postman to generate the Swift code.
I copy and paste the Swift code to my project, this is the code:
var semaphore = DispatchSemaphore (value: 0)
let parameters = "{ \n \"token\":\"MY_TOKEN\",\n \"resenha\": {\n \"codAP\":\"353750303020001\",\n \"codPropriedade\":\"0\",\n \"cpfVeterinario\":\"01787568814\",\n \"coordGeoLat\": \"37.421565\",\n \"coordGeoLong\": \"-122.084\",\n \"cpfCnpjProdutor\": \"89058500810\",\n \"dataNascimentoAnimal\": \"01/08/1981\",\n \"fotos\": null,\n \"graficas\": null,\n \"id\": \"1\",\n \"idComposicaoPelagem\": \"50\",\n \"idCorOlhoDir\": \"39902\",\n \"idCorOlhoEsq\": \"39902\",\n \"idEspecie\": \"5\",\n \"idPelagem\": \"6\",\n \"idRaca\": \"34\",\n \"idResenhaAnterior\":\"0\",\n \"idSexo\": \"2501\",\n \"machoCastrado\": \"N\",\n \"microChipAnimal\": \"123456989012377\",\n \"microchipMae\": \"\",\n \"nomeAnimal\": \"MACADANIAS111\",\n \"numeroAssocRaca\": \"\",\n \"numeroPassaporte\": \"\",\n \"outrasCaracteristicas\": null,\n \"quantAnimaisEq\": \"05\",\n \"quantAnimaisAs\": \"0\",\n \"quantAnimaisMu\": \"02\",\n \"retifica\": false\n }\n}"
let postData = parameters.data(using: .utf8)
var request = URLRequest(url: URL(string: "https://MY_ENDPOINT/validaResenha")!,timeoutInterval: Double.infinity)
request.addValue("Basic THIS_IS_BASIC_AUTH_VALIDATION", forHTTPHeaderField: "Authorization")
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("JSESSIONID=0000_B1PZRSVcyzEkDUkMxvk9ig:18jgnbg8n; LtpaToken2=q9JsIHVBKPsCYKGRohXJAKnXED3HRXXGlaswzYpnPSLS0B+c/WbiW+QcUMwmw/8xcb7VL1bVvbUh0ZAvMR3TNcGudWUkg9f0z5K0n0P2pJ5Frte6trqVLhPoKuI5E7zwC3Yg+XCsPBNFy0aukkrWNiCWAqbyGI3nir6UuX5qLER4H+bEYfk4cFw58eHGSIN/FTVjH7WW7aEAfkYNXxWzSDnNVJDtihZVXw+oJSfe74Vz8Scv33cPPZH2W74KvKwj09FOo+EJsvFcC2aDUQclYqwuo91HIaIpqcYb17cSCX95xn9KHErlC48M1bU03txKaDVcmUrOCrveCs7pVPNCz066cil5bzjXeYlXDlmUw5MT45Zgg8EmaJ9gi+iC2zPCU+W088OGEriphXpto40ww3irTN9rtnhIppB5U+drRFW6u25UmDkAjx899TNzC/XsJqkRXn9GinQv6xiD+Axnv3AgudQZBGyeYcNNJDRfA+jzbr6HE1NuNSkxY6aP1OvCLGkNDSA16chO7f4IjjR9jkvDH2m3+ajxa7as1rVNK9R0HHcZJLExvOJn3sJ1LKDwMNPTDjKGPLuWOVwTPQL2pzIitLjkeyx8A2Qcqo5p8U/+aa11Z/x8WA0bQRscZLWMtEHE6WLnHFqBaylCAzARF0Y5cOI/TYCW2xd99ux2WaJnumVlskr2uNCWdwSMOp78gvmzdmonQUH0Ko/k4wa8HcJPyMV5NK52gArIyGmeKXo=", forHTTPHeaderField: "Cookie")
request.httpMethod = "POST"
request.httpBody = postData
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else {
print(String(describing: error))
semaphore.signal()
return
}
print(String(data: data, encoding: .utf8)!)
semaphore.signal()
}
task.resume()
semaphore.wait()
print("end")
But I got now a 401 error. For some reason the basic Auth is not being accepted by the code or by the server.
I checked the user and password and they are correct.
I find out the fix for my problem.
I was using "http://MY_ENDPOINT/validaResenha" in my constants file instead of using "https://MY_ENDPOINT/validaResenha".
After add the "s" to "http" letter (it becomes "https") everything starts to work for me.
The strange part is that GET and PUT methods works fine because of the redirect from HTTP to HTTPS, but in the case of POST calls, I got this error.
It's fixed now.
iOS block HTTP use HTTPS or add to your Info.plist file
Im trying to post a http request with parameters, the response is XML Formatted.
see: https://www.codepunker.com/tools/http-requests/64243-brrjq9t
now, when I try to code that im always getting the same error 500. what I do wrong?
guard let url = URL(string: "http://www.nakdan.com/GetResult.aspx") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("text/xml; charset=utf-8", forHTTPHeaderField: "Content-Type")
var bodyData = "txt=מה קורה&usr=&pass=&ktivmale=false"
request.httpBody = bodyData.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request as URLRequest){
data,response, error in
print(response!)
if error != nil{
print("error")
return
}
}
task.resume()
I have the following code but consistently get error 401 forbidden when attempting to run it:
func email() {
let session = URLSession.shared
let request = NSMutableURLRequest(url: NSURL(string: "https://api.mailgun.net/v3/{edited_out}/messages")! as URL)
request.httpMethod = "POST"
let data = "from: Swift Email <(test#test.com)>&to: [my_email_address#gmail.com,(my_email_address#gmail.com)]&subject:Hello&text:Testing_some_Mailgun_awesomness"
request.httpBody = data.data(using: String.Encoding.ascii)
request.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.setValue("key-{edited_out}", forHTTPHeaderField: "api")
let task = session.dataTask(with: request as URLRequest, completionHandler: {(data, response, error) in
if let error = error {
print(error)
}
if let response = response {
print("url = \(response.url!)")
print("response = \(response)")
let httpResponse = response as! HTTPURLResponse
print("response code = \(httpResponse.statusCode)")
}
})
task.resume()
}
The error is:
url = https://api.mailgun.net/v3/{edited_out}/messages
response = <NSHTTPURLResponse: 0x600000226a20> { URL: https://api.mailgun.net/v3/{edited_out}/messages } { status code: 401, headers {
Connection = "keep-alive";
"Content-Length" = 9;
"Content-Type" = "text/html; charset=utf-8";
Date = "Thu, 29 Dec 2016 21:22:46 GMT";
Server = nginx;
"Www-Authenticate" = "Basic realm=\"MG API\"";
} }
response code = 401
If I send such a request via curl with my credentials it works fine.
Any ideas?
You need to set the uesrname and the password.
Something like this:
request.setValue("Basic \(base64Credentials)", forHTTPHeaderField: "Authorization")
and base64Credentials is the :
let credentials= String(format: "%#:%#", username, password)
let base64Credentials= credentials.data(using: String.Encoding.utf8)!
Finally got it working with the following code, if it helps anyone else:
func email() {
let session = URLSession.shared
let request = NSMutableURLRequest(url: NSURL(string: "https://api.mailgun.net/v3/{edited_out}/messages")! as URL)
request.httpMethod = "POST"
let credentials = "api:key-{omitted}"
request.setValue("Basic \(credentials.toBase64())", forHTTPHeaderField: "Authorization")
let data = "from: Swift Email <(test#test.com)>&to: [my_email_address#gmail.com,(my_email_address#gmail.com)]&subject:Hello&text:Testing_some_Mailgun_awesomness"
request.httpBody = data.data(using: String.Encoding.ascii)
let task = session.dataTask(with: request as URLRequest, completionHandler: {(data, response, error) in
if let error = error {
print(error)
}
if let response = response {
print("url = \(response.url!)")
print("response = \(response)")
let httpResponse = response as! HTTPURLResponse
print("response code = \(httpResponse.statusCode)")
}
})
task.resume()
}
extension String {
func fromBase64() -> String? {
guard let data = Data(base64Encoded: self) else {
return nil
}
return String(data: data, encoding: .utf8)
}
func toBase64() -> String {
return Data(self.utf8).base64EncodedString()
}
}
So I guess the answer from William would have worked with:
let base64Credentials = credentials.data(using: String.Encoding.utf8)!.base64EncodedString()
Instead of:
let base64Credentials = credentials.data(using: String.Encoding.utf8)!
Im attemping to make a HTTPRequest using NSURLSession. When I set the full url the request returns the correct data but when using parameters (NSJSONSerialization.dataWithJSONObject -> HTTPBody I get this error
error Domain=NSURLErrorDomain Code=-1005 "The network connection was lost."
is there something im doing wrong here?
let json = ["api_key": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"]
do {
let jsonData = try NSJSONSerialization.dataWithJSONObject(json, options: .PrettyPrinted)
let url = NSURL(string: "https://api.themoviedb.org/3/discover/movie")!
let request = NSMutableURLRequest(URL: url)
request.HTTPBody = jsonData
request.HTTPMethod = "GET"
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
let task = NSURLSession.sharedSession().dataTaskWithRequest(request){ data, response, error in
if error != nil{
print("Error -> \(error)")
return
}
do {
let result = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [String:AnyObject]
print("Result -> \(result)")
} catch {
print("Error -> \(error)")
}
}
task.resume()
} catch {
print(error)
}
}
This is not a duplicate! I looked at the suggested answer (none of them worked) before asking this question
In your case that issue can be solved by changing the request.HTTPMethod = "GET" to request.HTTPMethod = "POST"
You should not send HTTP Body in the get request, to send the data with the body you should change HTTPMethod to post
Note: Please check if this api method supports POST requests, if it don't support post you can't use it with http body/post, as per doc i only find 'get' request for the discover/movie which can be like this:
let url = NSURL(string: "http://api.themoviedb.org/3/discover/movie?api_key=YOUR_API_KEY")!
let request = NSMutableURLRequest(URL: url)
request.addValue("application/json", forHTTPHeaderField: "Accept")
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) { data, response, error in
if let response = response, data = data {
print(response)
print(String(data: data, encoding: NSUTF8StringEncoding))
} else {
print(error)
}
}
task.resume()
Ref: You can check more information from this url: http://docs.themoviedb.apiary.io/#reference/discover/discovermovie/get