Using POST and Auth with Firebase Database and Swift - swift

I need to use REST Api on my iOS app to post/retrieve data from my server. The app is set up with a key generator, which gets me a id token, and then I format my http request with the id. I know something about my url request is wrong, I just don't know what, but I get back a status code 400 whenever I run this. Any ideas where I went wrong?
func postToFB() {
let preURL = "https://myapp.firebaseio.com/"
let url = URL(string: "\(preURL)\(code)/calculatorDisplay.json?auth=\(idToken)")!
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "PUT"
let parameters: [String: Any] = [
"field1": env.field1,
"field2": env.field2
]
request.httpBody = parameters.percentEncoded()
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data,
let response = response as? HTTPURLResponse,
error == nil else { // check for fundamental networking error
print("postCalcDataButtons error", error ?? "Unknown error")
return
}
guard (200 ... 299) ~= response.statusCode else { // check for http errors
print("postCalcDataButtons = statusCode should be 2xx, but is \(response.statusCode)")
print("postCalcDataButtons response = \(response)")
return
}
let responseString = String(data: data, encoding: .utf8)
print("postCalcDataButtons responseString = \(String(describing: responseString))")
}
task.resume()
}
Here's the extensions that make the above code work:
extension Dictionary {
func percentEncoded() -> Data? {
return map { key, value in
let escapedKey = "\(key)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
let escapedValue = "\(value)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
return escapedKey + "=" + escapedValue
}
.joined(separator: "&")
.data(using: .utf8)
}
}
extension CharacterSet {
static let urlQueryValueAllowed: CharacterSet = {
let generalDelimitersToEncode = ":#[]#" // does not include "?" or "/" due to RFC 3986 - Section 3.4
let subDelimitersToEncode = "!$&'()*+,;="
var allowed = CharacterSet.urlQueryAllowed
allowed.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
return allowed
}()
}
Here's the response from the server:
<NSHTTPURLResponse: 0x60000110a0a0> { URL: https://calculonapp.firebaseio.com/zlwxrx/calculatorButtons.json?auth=authCode } { Status Code: 400, Headers {
"Access-Control-Allow-Origin" = (
"*"
);
"Cache-Control" = (
"no-cache"
);
Connection = (
"keep-alive"
);
"Content-Length" = (
77
);
"Content-Type" = (
"application/json; charset=utf-8"
);
Date = (
"Fri, 21 Aug 2020 21:16:43 GMT"
);
Server = (
nginx
);
"Strict-Transport-Security" = (
"max-age=31556926; includeSubDomains; preload"
);
} }

I was able to fix this issue by changing the http method when writing data to firebase to "PATCH", then adding JSONSerialization to encode my my upload before running the URLSession...this is the code I used:
if let jsonData = try? JSONSerialization.data(withJSONObject: json, options: []) {
URLSession.shared.uploadTask(with: request, from: jsonData) { (data, response, error) in
if let httpResponse = response as? HTTPURLResponse {
completion(httpResponse.statusCode)
}
}
.resume()
}
You can keep in the guard statements while you test to catch for http error codes and report them to you, but once my code was working I removed them.

Related

How to make a url post request which is returned by the function in Swift

Hi guys I am trying to contact my Rest API and get the data. I am successful in doing that but I want the function to return the string that it obtained.
This is why code so far:
private func getPost(one: String, two: String, link: String) {
let url = URL(string: link)!
var request = URLRequest(url: url)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
let parameters: [String: Any] = [
"parent" : one,
"original": two
]
request.httpBody = parameters.percentEncoded()
var responseString = ""
print("Sarcasm \(yourMessage) \(otherMessage) \(link)")
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data,
let response = response as? HTTPURLResponse,
error == nil else { // check for fundamental networking error
print("error", error ?? "Unknown error")
return
}
guard (200 ... 299) ~= response.statusCode else { // check for http errors
print("statusCode should be 2xx, but is \(response.statusCode)")
print("response = \(response)")
return
}
responseString = String(data: data, encoding: .utf8)!
print("responseString = \(responseString)")
// return responseString
}
task.resume()
}
Where :
extension Dictionary {
func percentEncoded() -> Data? {
return map { key, value in
let escapedKey = "\(key)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
let escapedValue = "\(value)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
return escapedKey + "=" + escapedValue
}
.joined(separator: "&")
.data(using: .utf8)
}
}
extension CharacterSet {
static let urlQueryValueAllowed: CharacterSet = {
let generalDelimitersToEncode = ":#[]#" // does not include "?" or "/" due to RFC 3986 - Section 3.4
let subDelimitersToEncode = "!$&'()*+,;="
var allowed = CharacterSet.urlQueryAllowed
allowed.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
return allowed
}()
}
All I want is this function (getPost) to return the response string that it obtains from the post request. However, I do not know what to do. I mean the application gets the response string from the post request but then I want to modify the function so that it returns it instead of printing it.

Swift 4.2 code equivalent for SAP's Leonardo API [duplicate]

This question already has answers here:
Upload image with parameters in Swift
(3 answers)
Closed 4 years ago.
Getting HTTP 400 error while connecting with SAP Leonardo sandbox server using scene text recognition API
SAP provides boilerplate code in older version of swift. I have used curl command and the API and works. But its not working when I try and convert the code into swift 4.2 version. I am attaching the code below.
func connectWithSAP(photoURL : URL, photoData : String, sentImageData : Data){
if let myNewURL = URL(string: "https://sandbox.api.sap.com/ml/scenetextrecognition/scene-text-recognition") {
var myRequest = URLRequest(url: myNewURL)
myRequest.addValue("multipart/form-data; --\(boundary)", forHTTPHeaderField: "Content-Type")
myRequest.addValue("application/json", forHTTPHeaderField: "Accept")
myRequest.addValue("xxxxxxxxxxx", forHTTPHeaderField: "APIKey")
myRequest.httpMethod = "POST"
myRequest.cachePolicy = .reloadIgnoringLocalCacheData
myRequest.timeoutInterval = 60.0
// Constructing the body of the request.
var data = Data()
var dataString = ""
dataString.append("--\(boundary)\r\n")
dataString.append(contentsOf: "Content-Disposition:form-data; name=\"files\"; filename=\"Image1.jpeg\" \r\n")
dataString.append(contentsOf: ";Content-Type:image/jpeg \r\n\r\n")
dataString.append(photoData)
dataString.append("--\(boundary) ----- \r\n")
data = dataString.data(using: .utf8)!
myRequest.httpBody = data
let task = URLSession.shared.dataTask(with: myRequest) { (data, response, error) in
if let error = error {
print(error)
}
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
print(error as Any)
// Getting output at this stage, which is shown below
return }
if let mimeType = httpResponse.mimeType,
mimeType == "application/json",
let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String:Any]
print(json as Any)
}catch {
print(error)
}
}
}
task.resume()
}
I am getting a following details in my response object
{ URL: https://sandbox.api.sap.com/ml/scenetextrecognition/scene-text-recognition } { Status Code: 400, Headers {\n Connection = (\n \"keep-alive\"\n );\n \"Content-Length\" = (\n 131\n );\n \"Content-Type\" = (\n \"application/json\"\n );\n Date = (\n \"Sat, 16 Feb 2019 11:56:37 GMT\"\n );\n Server = (\n \"Werkzeug/0.14.1 Python/3.5.5\"\n );\n \"Strict-Transport-Security\" = (\n \"max-age=31536000; includeSubDomains; preload;\"\n );\n \"X-Vcap-Request-Id\" = (\n \"fea7037c-4e48-49d2-4be1-53b0dad0ee46\"\n );\n}
As you would see the status code is HTTP 400. Need some help in getting the right response and data from the server.
Most probably, the body data got messed up. Here's the working code:
let boundaryConstant = "----WebKitFormBoundary7MA4YWxkTrZu0gW"
let headers = [
"APIKey": "YourAPIKEY"
]
let contentType = "multipart/form-data; boundary=" + boundaryConstant
//API endpoint for API sandbox
var request = URLRequest(url: URL(string: "https://sandbox.api.sap.com/ml/scenetextrecognition/scene-text-recognition")!)
//setting request method
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
let session = URLSession.shared
let path1 = Bundle.main.path(forResource: "your_image", ofType: "png")!
let url = URL(fileURLWithPath: path1)
let fileName = url.lastPathComponent
let data = try? Data(contentsOf: url)
let imageData = UIImage.init(data: data!)!
let pngData = UIImagePNGRepresentation(imageData)!
let mimeType = "image/png"
let boundaryStart = "--\(boundaryConstant)\r\n"
let boundaryEnd = "--\(boundaryConstant)--\r\n"
let fieldName = "files"
let contentDispositionString = "Content-Disposition: form-data; name=\"\(fieldName)\"; filename=\"\(fileName)\"\r\n"
let contentTypeString = "Content-Type: \(mimeType)\r\n\r\n"
var body = Data()
body.append(boundaryStart.data(using: .utf8)!)
body.append(contentDispositionString.data(using: .utf8)!)
body.append(contentTypeString.data(using: .utf8)!)
body.append(pngData)
body.append("\r\n".data(using: .utf8)!)
body.append(boundaryEnd.data(using: .utf8)!)
request.httpBody = body
request.setValue(contentType, forHTTPHeaderField: "Content-Type")
request.setValue(String(body.count), forHTTPHeaderField: "Content-Length")
let dataTask = session.dataTask(with: request) { (data, response, error) in
if (error != nil) {
print(error)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse)
}
}
dataTask.resume()
You can also use Alamofire to upload image. It's way cleaner, don't need to play around with "body" much:
let headers: HTTPHeaders = [
"APIKey": "<<Your API KEY>>",
"Content-type": "multipart/form-data"
]
let parameters:[String: String] = [:] //any other parameters you need to send
let path1 = Bundle.main.path(forResource: "<<your_image>>", ofType: "<<png or jpeg>>")!
let url = URL(fileURLWithPath: path1)
let fileName = url.lastPathComponent
let data = try? Data(contentsOf: url)
let imageData = UIImage.init(data: data!)!
//converting it into png data
let pngData = UIImagePNGRepresentation(imageData)
let mimeType = "image/png"
let fieldName = "files"
Alamofire.upload(multipartFormData: { (multipartFormData) in
for (key, value) in parameters {
multipartFormData.append("\(value)".data(using: String.Encoding.utf8)!, withName: key as String)
}
if let data = pngData{
multipartFormData.append(data, withName: fieldName, fileName: fileName, mimeType: mimeType)
}
}, usingThreshold: UInt64.init(), to: "https://sandbox.api.sap.com/ml/scenetextrecognition/scene-text-recognition" , method: .post, headers: headers) { (result) in
switch result{
case .success(let upload, _, _):
upload.responseJSON { response in
print("Succesfully uploaded")
}
case .failure(let error):
print("Error in upload: \(error.localizedDescription)")
}
}

get error 401 when post request using XMLParsing swift library

i'm testing XMLParsing library
(that use Codable protocol with XML request)
XMLParsing library link :
https://github.com/ShawnMoore/XMLParsing
with https://openweathermap.org API
the API link is
"http://api.openweathermap.org/data/2.5/weather"
my model is
struct weather:Codable {
let q : String
let appid : String
let mode : String
}
and the request is
var request = URLRequest(url: URL(string: "http://api.openweathermap.org/data/2.5/weather")!)
request.httpMethod = "POST"
let post2 = weather(q: "london", appid: "f4be702b940e5073d765cb2473f0b31b", mode: "xml")
do{
let body = try XMLEncoder().encode(post2, withRootKey: "current")
request.httpBody = body
} catch{}
let session = URLSession.shared
let task = session.dataTask(with: request) { data, response, error in
if error != nil {
print("error: \(String(describing: error))")// Handle error…
return
}
guard let data = data else {return }
print("response: \(response)")
print("data: \(data)")
}
task.resume()
I don't know where is the problem !
I always get error code 401
response: Optional(<NSHTTPURLResponse: 0x600003bdedc0> { URL: http://api.openweathermap.org/data/2.5/weather } { Status Code: 401, Headers {
"Access-Control-Allow-Credentials" = (
true
);
"Access-Control-Allow-Methods" = (
"GET, POST"
);
"Access-Control-Allow-Origin" = (
"*"
);
Connection = (
"keep-alive"
);
"Content-Length" = (
107
);
"Content-Type" = (
"application/json; charset=utf-8"
);
Date = (
"Mon, 14 Jan 2019 07:14:16 GMT"
);
Server = (
openresty
);
"X-Cache-Key" = (
"/data/2.5/weather?"
);
} })
data: 107 bytes
but on PostMan it working fine and get current data
Thanks to #OOPer
I put it as Parameters on the link and change the request to GET
it working fine now
import UIKit
import XMLParsing
struct current:Codable {
let city : City?
}
struct City:Codable {
let id : String?
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let request = URLRequest(url: URL(string: "http://api.openweathermap.org/data/2.5/weather?q=london&appid=f4be702b940e5073d765cb2473f0b31b&mode=xml")!)
let session = URLSession.shared
let task = session.dataTask(with: request) { (data, response, error) in
do{
guard (error == nil) else {
print("There is error")
return
}
guard let data = data else {return}
let newData = try XMLDecoder().decode(current.self, from: data)
print((newData.city?.id)!)
}catch let error {
print("there is error",error)
}
}
task.resume()
}
}

Swift API call with URLSession gives 504 error

I have an API call with oauth which I tested with correct authorization token in postman.I am getting proper response in postman. But when I try same thing in Swift, I get 504 error.
I have checked every params and headers properly and everything looks same as postman. Not sure why samething is working in postman and gives 504 error in swift. what could be issue?
var params = [String : String]()
params["Id"] = Id;
var headers = [String : String]()
headers["api-key"] = "XXXXXX"
headers["Authorization"] = "Bearer XXX"
do{
var request = URLRequest(url: URL(string: getURL())!)
request.allHTTPHeaderFields = headers
request.httpBody = try JSONSerialization.data(withJSONObject: params , options: [])
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
let httpResponse = response as! HTTPURLResponse
print(httpResponse)
}
task.resume()
}catch{
}
When using a GET request, there is no body to the request. Everything goes on the URL.
Also are you sure that in Postman you are using only those 2 headers?
See if something like this works for you:
var params: Parameters = Parameters()
params.updateValue(Id, forKey: "Id")
var components = URLComponents(string: getURL())!
components.queryItems = params.map { (key, value) in
URLQueryItem(name: key, value: value)
}
components.percentEncodedQuery = components.percentEncodedQuery?.replacingOccurrences(of: "+", with: "%2B")
let request = URLRequest(url: components.url!)
request.setValue("XXXXXX", forHTTPHeaderField: "api-key")
request.setValue("Bearer XXX", forHTTPHeaderField: "Authorization")
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else { // check for fundamental networking error
print("error=\(error)")
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(response)")
}
let responseString = String(data: data, encoding: .utf8)
print("responseString = \(responseString)")
}
task.resume()

Grab content from POST call URLSession

I am trying to pull the cookie from this POST call to use for another call.
I have a URLSession response for a POST call here:
<NSHTTPURLResponse: 0x600000418120> { URL: https://example.com:443/Auth/ } { Status Code: 200, Headers {
Accept = (
"*/*"
);
"Accept-Encoding" = (
"br, gzip, deflate"
);
"Accept-Language" = (
"en-us"
);
Authorization = (
"Basic keyHere=="
);
"Content-Encoding" = (
gzip
);
"Content-Type" = (
"application/json"
);
Date = (
"Tue, 25 Sep 2018 18:17:38 GMT"
);
Host = (
"example.com:443"
);
"SMSESSION-Idle-Expire" = (
"Tue, 25-Sep-2018 20:17:38 GMT"
);
"SMSESSION-Session-Expire" = (
"Wed, 26-Sep-2018 04:17:38 GMT"
);
"Set-Cookie" = (
"SMSESSION=sessionKeyHere==;Domain=example.com;Path=/"
);
"Transfer-Encoding" = (
Identity
);
"User-Agent" = (
"Web%20Service%20Test/1 CFNetwork/974.2.1 Darwin/18.0.0"
);
"X-Forwarded-For" = (
"11.111.11.11"
);
} }
How would I pull the Set-Cookie SMSESSION key from here? Is there a correct way to do this? Or would I just parse through this response and pull the key from there?
Here is my function:
func test() {
let username = "myUsername"
let password = "myPassword"
let loginString = String(format: "%#:%#", username, password)
let loginData = loginString.data(using: String.Encoding.utf8)!
let base64LoginString = loginData.base64EncodedString()
let url = URL(string: "https://example.com:443/Auth/")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Basic \(base64LoginString)", forHTTPHeaderField: "Authorization")
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let response = response {
print(response)
}
if let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
print(json)
} catch {
print(error)
}
}
}.resume()
}
Thanks for the help!
Thanks #Cristik for the answer.
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let response = response {
let httpResponse = response as! HTTPURLResponse // HTTPURLResponse is a subclass of URLResponse
print(httpResponse.allHeaderFields["Set-Cookie"] ?? "") // Cookie Value
}
if let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
print(json)
} catch {
print(error)
}
}
}.resume()