get error 401 when post request using XMLParsing swift library - swift

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()
}
}

Related

Using POST and Auth with Firebase Database and 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.

POST to Web API always returns nil

I'm just learning Web APIs with Swift.
And I've done as follows
Make some basic configuration
let session = URLSession(configuration: .default)
// Prepare URL #the URL is virtual now
let url = URL(string: "http://something.com/api")
guard let requestUrl = url else { fatalError() }
// Prepare URL Request Object
var request = URLRequest(url: requestUrl)
request.httpMethod = "POST"
Set Post parameter
let parameters: [String: String] = [
"user_ID": "1",
]
let jsonData = try JSONEncoder().encode(parameters)
request.httpBody = jsonData
Take request to our web api
// Perform HTTP Request
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
// Check for Error
if let error = error {
print("Error took place \(error)")
return
}
guard let data = data else { return }
do {
let myEnterprise = try JSONDecoder().decode(Enterprise.self, from: data)
print("Response data:\n \(myEnterprise)")
print("Response data:\n \(myEnterprise.returnData)")
} catch let jsonErr {
print(jsonErr)
}
}
task.resume()
myEnterprise is always nil. Can anyone help me?

unable to have a result while calling a rest API with Swift

I try to make a small command line program in swift 5 to call a Rest Web service
func callRest() {
URLCache.shared = URLCache(memoryCapacity: 0, diskCapacity: 0, diskPath: nil)
let todoEndpoint: String = "http://myURL"
print (todoEndpoint)
guard let url = URL(string: todoEndpoint) else {
print("Error: cannot create URL")
exit(1)
}
// private let apiKey = "my API Key"
let config = URLSessionConfiguration.default
config.httpAdditionalHeaders = [
"Accept": "application/json",
"API-Key": "my API Key"
]
var session = URLSession.shared
session = URLSession(configuration: config)
let urlRequest = URLRequest(url: url)
let task = session.dataTask(with: urlRequest) { (data, response, error) in
print ("task")
// check for any errors
guard error == nil else {
print("error calling GET on /todos/1")
print(error!)
return
}
// make sure we got data
guard let responseData = data else {
print("Error: did not receive data")
return
}
// check the status code
guard let httpResponse = response as? HTTPURLResponse else {
print("Error: It's not a HTTP URL response")
return
}
print (httpResponse)
}
task.resume()
// Response status
}
print ("debut")
callRest()
print("fin")
And I never see a result...
the line print ("task") is never display
Any help appreciate.
I think I don't really understand the task.resume usage...
(same code work in a playground)

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()

Reachability in Swift 3

I am new at this and can not seem to figure out how to use NSURLSession dataTaskWithResult:completion handler replacing NSURLConnection.sendSynchronusRequest(request as URLRequest, returning: &response
in the following code. The latter has been depreciated.
public class Reachability {
class func isConnectedToNetwork() -> Bool {
var status:Bool = false
let url = NSURL(string: "https://google.com")
let request = NSMutableURLRequest(url: url! as URL)
request.httpMethod = "HEAD"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringLocalAndRemoteCacheData
request.timeoutInterval = 10.0
var response:URLResponse?
do {
let _ = try NSURLConnection.sendSynchronousRequest(request as URLRequest, returning: &response) as NSData?
}
catch let error as NSError {
print(error.localizedDescription)
}
if let httpResponse = response as? HTTPURLResponse {
if httpResponse.statusCode == 200 {
status = true
}
}
return status
}
}
Simple solution with a completion handler
class func isConnectedToNetwork(completion: #escaping (Bool) -> ()) {
let url = URL(string: "https://google.com")!
var request = URLRequest(url: url)
request.httpMethod = "HEAD"
request.cachePolicy = .reloadIgnoringLocalAndRemoteCacheData
request.timeoutInterval = 10.0
URLSession.shared.dataTask(with:request) { (data, response, error) in
if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 {
completion(true)
} else {
completion(false)
}
}.resume()
}
And to call
Reachability.isConnectedToNetwork { success in
if success {
// google is up
} else {
// google is down.
}
}