Retrieving web string in Swift - 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
}
}

Related

make a POST request with json data

I have a problem on my code that I can't figure it out, I am making a POST request to a endpoint that is used for log-in and I am not receiving anything back, But when I send the same request to Postman I receive the valid data so the problem is at my code and truly I have no clue how to get this one fixed now friends.
The code:
func SignIn() {
let json: [String: Any] = ["User": "test", "Password": "test123"]
let jsonData = try? JSONSerialization.data(withJSONObject: json, options: .fragmentsAllowed)
let url = URL(string: "https://testingkrupi/Signin")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Accept")
request.httpBody = jsonData
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print(error?.localizedDescription ?? "No data")
return
}
let responseJSON = try? JSONSerialization.jsonObject(with: data, options: .allowFragments)
if let responseJSON = responseJSON as? [String: Any] {
print(responseJSON)
}
}
task.resume()
}
The request that must be send:
POST http://testingkrupi/Signin HTTP/1.1
User-Agent: Fiddler
Host: localhost
Content-Length: 59
Content-Type: application/json; charset=utf-8
{
"User": "test",
"Password": "test123"
}
The response I must get:
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/10.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Thu, 14 Apr 2022 07:00:29 GMT
Content-Length: 351
{
"StatusCode": 0,
"Result": {
"First": "test",
"Last": "test123",
"CompleteName": "test test123",
"PhoneNumber": "+512 512321 125",
"Email": "randomtest#gmail.com",
"IsConnectedToCustomer": true,
"Token": "423tj32o3jg230g923gj023gijf2o02",
"TokenExpireDate": "2020-05-14T09:00:29.2"
}
}
This answer is more about debugging skill you need to have in order to advance.
First, you need to find where lies the issue. You know it's in SignIn() (I guess).
Now, let's start by adding some informations to know what's happening, if you don't know (yet) how to use breakpoints, at least add logs to know what's happening:
let task = URLSession.shared.dataTask(with: request) { data, response, error in
print("HTTPResponse: \(response)")
guard let data = data, error == nil else {
print(error ?? "No data")
return
}
let responseJSON = try? JSONSerialization.jsonObject(with: data, options: .allowFragments)
if let responseJSON = responseJSON as? [String: Any] {
print(responseJSON)
} else {
//Here you test nullability of responseJSON AND if it's a [String: Any], so what if it's not null, but not a [String: Any]?
print("Response JSON is nil, or is not a [String: Any]")
}
}
That should at least give you some feedbacks, which one is printed.
Now, let's continue:
Each time you do a try? (with the question mark), you are saying this: I know that the method can throw an error, and error which often has a good explanation on why it failed exactly, helping me into debugging it, but I decided to not care about it, I'll just ignore it.
That's the same as ignoring the Gas/Fuel tank empty warning on your car, don't complain later that it was beeping, but you just ignore it when the car will stop because there is no more gas/fuel.
You can use then a try! (with exclamation mark), which will show the error in console, but will make crash the app. At least you shouldn't miss it.
But, it's recommended for try to implement a proper do/try/catch:
do {
let responseJSON = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
} catch {
print("Error while calling JSONSerialization: \(error)")
print("Got stringified response: \(String(data: data, encoding: .utf8) ?? "not utf8 stringifiable data")")
//If you enter in the "not utf8 stringifiable data" output, then, check the data, maybe use hex representation, since not all data, like jpg data can be UTF8 interpreted as such, but that's another case.
}
For now, I don't know the output in console, but:
print("HTTPResponse: \(response)") might give you important info, usually if it's not a 200 HTTP code, and you'll have at least a "sure" print in console to ensure that the closure is correctly called.
print("Error while calling JSONSerialization: \(error)") & print("Got stringified response: \(String(data: data, encoding: .utf8) ?? "not utf8 stringifiable data")") should give you info on the real data you are receiving, and maybe why you are receiving that one.
Now, why I printed the "Stringified response" & HTTPResponse? Simply because developers often mistake what they should received with what they will receive, and ignore totally what they are in fact receiving. Don't be like that, read what you are receiving, and fix your issues. Simply reading the doc and even if you implement it correctly doesn't mean that you'll necessary succeed in first attempt, from an error on your implementation call, a server issue, an outdated doc, etc, everything can occurs.
Let's keep digging, you are using POSTMAN and it's working, did you know that POSTMAN can generate Swift URLSession Code '(and cURL too, which is often also useful)? It's not "beautiful Swift" code, but it's working one. Test that one, and if it's working, compare it with you own implementation. It can show a forgotten parameter, any silly mistake or misunderstanding of the API documentation.
Unrelated but:
You should start naming your methods with a lower case:
func SignIn()
->
func signIn()
let jsonData = try? JSONSerialization.data(withJSONObject: json, options: .fragmentsAllowed)
There is no need for .fragmentsAllowed here. You aren't using a String at top level, it's a Dictionary.

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

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=.

Get header data from a request response in 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
}
}

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