Get header data from a request response in swift - swift

I want to get X-Dem-Auth in a header request with swift to stock that in my app.
See the response :
headers {
"Content-Length" = 95;
"Content-Type" = "application/json; charset=utf-8";
Date = "Fri, 15 Apr 2016 08:01:58 GMT";
Server = "Apache/2.4.18 (Unix)";
"X-Dem-Auth" = null;
"X-Powered-By" = Express;

If the response is type of NSHTTPURLResponse you can get header from response.allHeaderFields
As apple documentation says :
A dictionary containing all the HTTP header fields received as part of the server’s response. By examining this dictionary clients can see the “raw” header information returned by the HTTP server.
The keys in this dictionary are the header field names, as received from the server. See RFC 2616 for a list of commonly used HTTP header fields.
So to get for example a X-Dem-Auth in response header you can access it in that way :
if let httpResponse = response as? NSHTTPURLResponse {
if let xDemAuth = httpResponse.allHeaderFields["X-Dem-Auth"] as? String {
// use X-Dem-Auth here
}
}
UPDATE
Updated due to comment from Evan R
if let httpResponse = response as? HTTPURLResponse {
if let xDemAuth = httpResponse.allHeaderFields["X-Dem-Auth"] as? String {
// use X-Dem-Auth here
}
}

Update for iOS 13 and above.
I suggest if the response is of type HTTPURLResponse and you want get to a specific header value only. Then below is a better approach.
if let httpResponse = response as? HTTPURLResponse {
if let xDemAuth = httpResponse.value(forHTTPHeaderField: "X-Dem-Auth") as? String {
// use X-Dem-Auth here
}
}

Related

How to Call HTTP Get and Save in Variable In Swift?

My question is simple: how do you call a HTTP GET Request in Swift?
I am trying to retrieve specific data from server (I have the URL string), the problem is that the previous answers I saw, doesn't explain thoroughly how to request an HTTP Get and save the retrieved information in a variable to be used later? Thanks in advance!
Here's what I have so far:
let myURL = NSURL(string:"https://api.thingspeak.com/channels/CHANNEL_ID/last_entry
_id.txt");
let request = NSMutableURLRequest(url:myURL! as URL);
request.httpMethod = "GET"
Not sure what do following requesting the GET.
In your post you are missing the part that does the actual getting to of the data.
Your code should look something like this to get the value out of the text file.
var lastID: String?
let myURL = NSURL(string:"https://api.thingspeak.com/channels/1417/last_entry_id.txt");
let request = NSMutableURLRequest(url:myURL! as URL);
//request.httpMethod = "GET" // This line is not need
// Excute HTTP Request
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
// Check for error
if error != nil
{
print("error=\(error)")
return
}
// Print out response string
let responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
print("responseString = \(responseString!)")
lastID = "\(responseString!)" // Sets some variable or text field. Not that its unwrapped because its an optional.
}
task.resume()

(Swift) How to check if content-encoding is gzip

Is there any way in Swift to find out if data from a request returns in gzip?
I want to write a test method to check if Content-Encoding returns gzip file:
You can get this info from the header fields in the HTTPURLResponse:
URLSession.shared.dataTask(with: url) { (data, response, error) in
if let response = response as? HTTPURLResponse {
if let encoding = response.allHeaderFields["Content-Encoding"] as? String {
print(encoding)
print(encoding == "gzip")
}
}
}.resume()
Note that this downloads the headers and the data.
If you want to only get the headers without downloading the data, a better solution is to use an URLRequest set to "HEAD" like this:
var request = URLRequest(url: url)
request.httpMethod = "HEAD"
URLSession.shared.dataTask(with: request) { (_, response, _) in
if let response = response as? HTTPURLResponse {
if let enc = response.allHeaderFields["Content-Encoding"] as? String {
print(enc)
print(enc == "gzip")
}
}
}.resume()
This way, only the headers are downloaded.
If you read the first 4 bytes of the file, you should get a Magic Number
So you should be able to do something like:
var magicNumber = [UInt](count: 4, repeatedValue: 0)
data.getBytes(&magicNumber, length: 4 * sizeof(UInt))
If it's GZipped, it will have a Magic Number of 1f 8b, so check for that.

Cloudant function-clause error at HTTP GET request

This is my first question here and I have not much experience in coding so please bear with me. Thanks!
I defined some documents in my Bluemix Cloudant account with different cars which have different characteristics. I want to get one entry from an IOS Swift front-end App.
This is an example query url:
https://$ACCOUNT-bluemix.cloudant.com/cars/_design/car_index/_search/car_index_name?q=size:small
Now the problem: If I use this url in a browser I get the correct results in JSON format back without any error. But if the app makes the request a function-clause error is logged while the request itself seems to be successful.
I read that a function_clause error is caused by some bug in the Javascript Cloudant uses for indexing the documents. The Javascript I'm using is exactely the same as Cloudant states it in the tutorials.
Has anyone an idea why it works in the browser but not in the App?
Thank you very much for any help!
Here is all the code:
This is the method I use in swift to make the request:
func databaseRequest(size: String, interior: String, fuel: String) {
let baseURL = "https://$ACCOUNT-bluemix.cloudant.com/cars/_design/car_index/_search/car_index_name?q="
let queryURL = "size:\(size)"
let completeURL: String = baseURL + queryURL
let completeURLModified = completeURL.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)
let requestURL = URL(string: completeURLModified!)
var request = URLRequest(url: requestURL!)
request.httpMethod = "GET"
request.setValue("Basic \(credentials)", forHTTPHeaderField: "Authorization")
let task = URLSession.shared.dataTask(with: request){data, response, error in
guard error == nil else{
print("There was an error:", error as Any)
return
}
guard data == data else{
print("Data is empty")
return
}
let jsonResponse = try! JSONSerialization.jsonObject(with: data!, options: [])
print("This is JSON Response", jsonResponse)
}; task.resume()
}
This is the response from the JSON answer:
This is JSON Response {
error = "unknown_error";
reason = "function_clause";
ref = 1944801346;
}
The rest of log from http headers if this is helpful:
Optional(<NSHTTPURLResponse: 0x6080000349c0> { URL: https://$ACCOUNT-bluemix.cloudant.com/cars/_design/car_index/_search/car_index_name?q=size:small } { status code: 500, headers {
"Cache-Control" = "must-revalidate";
"Content-Length" = 70;
"Content-Type" = "application/json";
Date = "Thu, 24 Nov 2016 04:41:03 GMT";
Server = "CouchDB/2.0.0 (Erlang OTP/17)";
"Strict-Transport-Security" = "max-age=31536000";
Via = "1.1 lb1.bm-cc-dal-01 (Glum/1.31.3)";
"X-Cloudant-Backend" = "bm-cc-dal-01";
"X-Content-Type-Options" = nosniff;
"X-Couch-Request-ID" = 51e5e0b5e1;
"X-Couch-Stack-Hash" = 1944801346;
"X-CouchDB-Body-Time" = 0;
Last but not least the Javascript file I use as Index in the design document in Cloudant:
function (doc) {
index("name", doc.name, {"store": true});
if (doc.fuel){ index("fuel", doc.fuel, {"store": true});}
if (doc.interior){ index("interior", doc.interior, {"store": true});}
if (doc.size){index("size", doc.size, {"store": true});
}}
I think this error is due to cloudant trying to decode whatever you passed as \(credentials) as a base64 encoded string. If \(credentials) is not a valid base64 encoded string (e.g. contains characters other than a-z, A-Z, 0-9, +, / and =), then my guess is that cloudant's base64 decoding function fails with the above error.
You need to make sure that \(credentials) is the string <your_username>:<your_password> encoded correctly. E.g. if your username is john and your password is doe, then \(credentials) should be am9objpkb2U=.

Retrieving web string in Swift

I'm having a hard time retrieving data when I post to my web service using Swift.
All I get is the number of characters I am returning, not the actual characters. I figure they must be there, I just don't know how to retrieve them.
Here is my Swift 3 code:
let url: NSURL = NSURL(string: "http://www.nanorig.dk/api/login")!
let request:NSMutableURLRequest = NSMutableURLRequest(url:url as URL)
let bodyData = "data=something"
request.httpMethod = "POST"
request.httpBody = bodyData.data(using: String.Encoding.utf8);
NSURLConnection.sendAsynchronousRequest(request as URLRequest, queue: OperationQueue.main)
{
(response, data, error) in
print("response: \(response)")
print("data: \(data)")
print("error: \(error)")
}
When I run this code, I get the following output:
response: Optional(<NSHTTPURLResponse: 0x174225400> { URL: http://www.nanorig.dk/api/login } { status code: 200, headers {
"Accept-Ranges" = bytes;
Age = 0;
"Cache-Control" = "no-store, no-cache, must-revalidate, post-check=0, pre-check=0";
Connection = "keep-alive";
"Content-Encoding" = gzip;
"Content-Length" = 85;
"Content-Type" = "text/html; charset=UTF-8";
Date = "Thu, 06 Oct 2016 13:15:18 GMT";
Expires = "Thu, 19 Nov 1981 08:52:00 GMT";
Pragma = "no-cache";
Server = Apache;
"Set-Cookie" = "PHPSESSID=6so30t6brvj6j5k03kihr1m1f5; path=/";
Vary = "Accept-Encoding";
Via = "1.1 varnish-v4";
"X-Powered-By" = "PHP/5.6.26";
"X-Varnish" = 159701416;
} })
data: Optional(45 bytes)
error: nil
2016-10-06 15:15:19.428231 NanOrig[2245:1192962] [MC] System group container for systemgroup.com.apple.configurationprofiles path is /private/var/containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles
2016-10-06 15:15:19.436932 NanOrig[2245:1192962] [MC] Reading from public effective user settings.
Notice, especially, the following line in the above:
data: Optional(45 bytes)
The method being run on the server is a simple PHP line:
echo 'just a test string to return to the swift app';
...this is 45 characters...
So, the way I see it, the "data" variable should somehow contain what is echoed; how else should it know the number of characters?
I just don't know how to get them out, and I have tried everything I can think of (putting the method call in a variable, assigning the data value to something else, casting it to a string, looked for a toString method, Googled and searched here for similar problems, even tried a completely different code snippet from the web, which yielded the same problem).
I have code elsewhere in my application that returns string from the server with simple GET requests, however, this needs to be a POST - I'm just not posting anything yet, since it doesn't seem to work.
I really hope someone can tell me what is going on.
You should be able to cast that Data to an NSString easily using NSString(data: data, encoding: NSUTF8StringEncoding).
To unwrap the resulting string (as this is a failable initialiser), do the following:
if let unwrappedDataString = dataString {
// Do things with the unwrapped string.
}
EDIT:
To perfectly write this for you, use the following. Your comment had you force unwrapping data which means if for whatever reason data doesn't exist, your app will crash.
if let unwrappedData = data {
if let dataString = NSString(data: unwrappedData, encoding: String.encoding.utf8.rawValue) {
// Do stuff
}
}

NSURLConnection response returns 500 Status Code

I am trying to connect to a local node.js server setup and authenticate the user. I keep getting the 500 status code and can't figure out what I am missing.
I have tried hitting the server with these credentials from a web browser, and it works as expected.
Note: I do understand I have to use the NSURLSession instead of NSURLConnection, but for now, I need to get this to work.
Here is my code,
func signInUserWithDetails(userName:String,userPassword:String,serverURL:NSURL) {
let credDic :[String:String]=["user[name]":userName,
"user[password]":userPassword ]
self.httpMethod="PUT"
self.httpPath="/account"
self.expectedStatusCode=201
self.actualStatusCode=NSNotFound
self.requestUniqueIdentifier = NSUUID().UUIDString
let urlComponents = NSURLComponents(URL: serverURL, resolvingAgainstBaseURL: false)!
urlComponents.path = httpPath
let formedURL = urlComponents.URL!
var requestOrg = NSMutableURLRequest(URL: formedURL)
requestOrg.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
requestOrg.addValue("application/json", forHTTPHeaderField: "Accept")
requestOrg.HTTPMethod=self.httpMethod!
print(requestOrg.allHTTPHeaderFields) // Output 1
do{
let theJSONData = try NSJSONSerialization.dataWithJSONObject(credDic,options: NSJSONWritingOptions.PrettyPrinted)
let theJSONText = NSString(data: theJSONData,encoding: NSASCIIStringEncoding)
requestOrg.HTTPBody = theJSONData;
let tempD=try NSJSONSerialization.JSONObjectWithData(requestOrg.HTTPBody!, options: []) as? [String:String]
print("\(tempD)") //Output 2
}catch let error as NSError {
print(error)
}
connection = NSURLConnection(request: requestOrg, delegate: self, startImmediately: true)!
}
And I am just printing out the response with this,
func connection(didReceiveResponse: NSURLConnection, didReceiveResponse response: NSURLResponse) {
print("----------------------didReceiveResponse")
self.response=response
print("Response Received:"+"\(self.response)")
let urlResponse:NSHTTPURLResponse = response as! NSHTTPURLResponse
let responseCode=urlResponse.statusCode
self.actualStatusCode=responseCode
}
And the result I get is
Optional(["Accept": "application/json", "Content-Type": "application/x-www-form-urlencoded"])
Optional(["user[password]": "R", "user[name]": "R"])
----------------------didReceiveResponse
Response Received:Optional(<NSHTTPURLResponse: 0x7faba269d440> { URL: http://localhost:3000/account } { status code: 500, headers {
Connection = "keep-alive";
"Content-Length" = 1464;
"Content-Type" = "application/json";
Date = "Sat, 26 Dec 2015 08:34:45 GMT";
"X-Powered-By" = Express;
} })
And the didReceiveData throws this error
{"error":{"message":"Cannot read property 'name' of undefined","stack":"TypeError: Cannot read property 'name' of undefined\n at Object.exports.signIn [as handle] ( .......
Status code 500 means, that the server could not process your data and ran into an internal error. This oftentimes is caused by improperly encoded HTTP messages, where the server was unable to catch all possible errors.
When looking at your code, it becomes immediately apparent:
You are not sending a properly application/x-www-form-urlencoded encoded data to the server. This is likely the main cause of your problem. The other cause might be, that it's likely not a PUT but a POST method which is required to sign-in.
But before explaining how you encode your data properly, I would suggest to find out whether your server accepts JSON as content data (application/json). If so, properly encoding the data is much easier: having a JSON object (your variable credDic), simply convert it to JSON as UTF-8 in a NSData container. Then, get the length in bytes, set headers Content-Type and Content-Length accordingly.
I had a similar issue but after tried to include Content-Type using application/json, it was solved.
Example: request.addValue("application/json", forHTTPHeaderField: "Content-Type")
The client application gets an HTTP status code of 500 with the message "Internal Server Error" as a response for API calls. The 500 Internal Server error could be caused by an error during the execution of any policy within Edge or by an error on the target/backend server.
The HTTP status code 500 is a generic error response. It means that the server encountered an unexpected condition that prevented it from fulfilling the request. This error is usually returned by the server when no other error code is suitable