Im learning about Swift and i trying to make an HTTP request. My code is working but i dont know how to return the result of the request:
func makeRequest(request: URLRequest)->String{
let task = URLSession.shared.dataTask(with: request){data, response, error in
guard let data = data, error == nil else{
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)")
}
print (data)
do {
let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
print(json)
} catch {
print("error serializing JSON: \(error)")
}
//print("responseString = \(responseString)")
}
task.resume()
return "Something"//i need to return the json as String
}
Someone can help me please? i was trying using CompletionHanlder but all the example that i found is based on swift 2, and this causes error on my code
The completion handler's type needs to be something like this:
#escaping ({argument types...})->{result type}
#escaping is needed as the completion handler is executed later when the communication is completed.
{argument types...} needs to be the types you want to pass to the handler, so in your case, a single type String. And you usually do not use the result from the handler, so you need to specify Void (aka ()).
Thus your completion handler's type needs to be:
#escaping (String)->Void
So, your method header becomes:
(You know you need a closing parenthesis for argument list.)
func makeRequest(request: URLRequest, completion: #escaping (String)->Void)
Whole your method would be something like this:
func makeRequest(request: URLRequest, completion: #escaping (String)->Void) {
let task = URLSession.shared.dataTask(with: request) {data, response, error in
guard let data = data, error == nil else{
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)")
}
print(data as NSData) //<-`as NSData` is useful for debugging
do {
let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
print(json)
//Why don't you use decoded JSON object? (`json` may not be a `String`)
} catch {
print("error serializing JSON: \(error)")
}
//Not sure what you mean with "i need to return the json as String"
let responseString = String(data: data, encoding: .utf8) ?? ""
completion(responseString)
}
task.resume()
}
You can use it as:
makeRequest(request: request) {response in //<-`response` is inferred as `String`, with the code above.
print(response)
}
func makeRequest(request: URLRequest, completion: (result : String?)->() {
let task = URLSession.shared.dataTask(with: request){data, response, error in
guard let data = data, error == nil else{
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)")
}
print (data)
do {
let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
print(json)
} catch {
print("error serializing JSON: \(error)")
}
completion("yourResultString")
//print("responseString = \(responseString)")
}
task.resume()
}
to call it
makeRequest(request: request) { (result : String?) in
if let result = result {
print("got result: \(result)")
}
You can't "return" the result of the request. By the time you have a result your makeRequest function has already returned to its caller. You should:
Change makeRequest to not return anything, because there's no
point
Replace the commented-out print statement with code that does
something with the responseString result.
Related
Here is my code I am using( the login part executes successfully, "Logged in successfully" is readable in the console )
#IBAction func loginAction(_ sender: Any) {
print("loginIsExecuting")
Server.Login(email: inputEmail.text!, password: inputPassword.text!, handlerDone:{
print("Logged in successfully")
Help.getData();
}, handlerFailed: {
print("===Failed to log in ")
DispatchQueue.main.sync {
self.displayAlert()
}
})
}
Help.swift
import Foundation
class Help {
static func getData(){
let urlString = URL(string: "http://192.168.1.16/~peterlizak/Secway/web/app/get-auditor")
if let url = urlString {
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error)
} else {
let responseString = String(data: data!, encoding: .utf8)
print(responseString) //JSONSerialization
}
}
task.resume()
}
}
}
While executing I am getting a 500 Internal Server Error.
This is from the apache log :
192.168.1.16 - - [13/Oct/2017:12:04:30 +0200] "POST /~Myname/Myapp/web/app/get-auditor HTTP/1.1" 500 495
Using the same code only calling Help.getData() after Server.login (not in the callback function) is working when I execute the code only once. But if I click the button again to trigger loginAction() ,again the function behaves as it does in the first version: the login executes, "Logged in successfully" is in the console, but when the code tries to execute the HTTP request it gets the same HTTP 500 error.(Also that solution is not acceptable because I must call first login and after the data)
I am using symphony for the backend and this is my simple code I try to request :
/**
* #Route("/get-auditor" , name="get_auditor")
*/
public function getAuditorInfoAction(Request $request)
{
$data = array(
'status' => 1,
'description' => 1,
"header" => 1
);
return new JsonResponse($data);
}
EDIT:
Server.Login
static func Login(email: String , password: String ,handlerDone:#escaping () -> Void,handlerFailed:#escaping () -> Void) {
print("=== Server Login ===")
let url = URL(string: "\(self.baseUrl)~peterlizak/Secway/web/app/login")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
let parameters = ["email": email, "password": password]
do {
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
} catch let error {
print(error.localizedDescription)
}
CallService(request: request, handlerDone:{ json in
ParseLoginResponse(json: json)
handlerDone()
}, handlerFailed: {
handlerFailed()
} )
}
Server.CallService
static func CallService(request:URLRequest,handlerDone: #escaping (_ json:[String:Any]) -> Void,handlerFailed: #escaping () -> Void)
{
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=\(String(describing: error))")
handlerFailed()
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)")
handlerFailed()
return
}
let responseString = String(data: data, encoding: .utf8)
var json:[String:Any]
do {
json = (try JSONSerialization.jsonObject(with: data, options: [])) as! [String:Any]
}
catch {
print("json error calling "+(request.url?.absoluteString)!+" =\(String(describing: error))")
print("responseString = \(responseString!)")
handlerFailed()
return
}
let status = json["status"] as! Int
if status != 0{
handlerDone(json)
}
else{
print("status is 0 = \(responseString!)")
handlerFailed()
}
}
task.resume()
}
HTTP 500 is an internal server error and not a client error. There will be some exception in related method in server for this API call. This error usually means something goes wrong in the server and need to look into the error trace.
You are mostly likely sending request to the wrong url since in the code you have /~peterlizak/Secway and error response has /~Myname/Myapp
Please review my code am badly stuck on this issue.
JSON conversion is not happening & it is going into catch block & printing following error.
Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.}
I've tried everything which is suggested here on StackOverflow but no luck.
I've trimmed my code for better understanding.
import Foundation
class Server
{
class func convertStringToDictionary(_ data: Data) -> [String:Any]?
{
do
{
let convertedDict = try JSONSerialization.jsonObject(with: data, options: []) as? [String:Any]
return convertedDict
}
catch let error as NSError
{
print(error)
}
return nil
}
class func registerUser( userInfo: String)
{
let url = URL(string: "http://132.148.18.11/missedprayers/welcome/register")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
let postString = "request=" + userInfo
request.httpBody = postString.data(using: .utf8)
//----------------------------------------
let task = URLSession.shared.dataTask(with: request)
{
data, response, error in
guard let data = data, error == nil
else
{
print("error=\(error)")
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200
{
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(response)")
}
//--------------
let finalData = Server.convertStringToDictionary(data)
print(finalData)
}
task.resume()
}
func submitBtnTapped(_ sender: AnyObject)
{
let userInfoDict = [
"name":"Maaz Patel",
"phoneNum":"+91899885623",
"email":"maaz#gmail.com",
"city":"pune",
"country":"India",
"dobEnglish":"11-02-1992",
"app":"Dalail",
"aqeeda":"Sufi",
"gender":"male",
"MCCycle":""
]
//-------------------
do
{
let jsonData = try JSONSerialization.data(withJSONObject: userInfoDict, options: [] )
let jsonStr = String.init(data: jsonData, encoding: String.Encoding.utf8)
Server.registerUser(userInfo: jsonStr!)
}
catch let error as NSError
{
print(error)
}
}
}
Consider "SubmitBtnTapped" function is getting called from somewhere.
When am trying on Postman it's working also on Android same web service is working fine.
I am doing some webscraping and am able to get the html source code off of my desired page, but I am casting it to a NSString. When I try to convert that to JSON, I get a null when I try to print it. When I print responseString2 I am seeing the source code, but as an NSString. What am I doing wrong?
let task2 = URLSession.shared.dataTask(with: request2 as URLRequest) { data2, response, error in
guard error == nil && data2 != nil else { // check for fundamental networking error
print("error2=\(error)")
return
}
if let httpStatus = response as? HTTPURLResponse , httpStatus.statusCode != 200 { // check for http errors
print("statusCode2 should be 200, but is \(httpStatus.statusCode)")
print("response2 = \(response)")
}
let responseString2 = NSString(data: data2!, encoding: String.Encoding.utf8.rawValue)
print("hthpage \(responseString2)")
if let dataFromString = responseString2?.data(using: String.Encoding.utf8.rawValue, allowLossyConversion: false){
let json = JSON(data: dataFromString)
print("JASON \(json)")
}
Don't convert data->string->data.
Try this code
and check the error if the error occurred when converting data to json.
URLSession.shared.dataTask(with: request!) { (d, response, error) in
if let data = d
{
if let str = String(data: data, encoding: String.Encoding.utf8)
{
print("data->string : " + str)
}
do
{
if let obj = try JSONSerialization.jsonObject(with: data, options: []) as? [String:AnyObject]
{
DispatchQueue.main.async {
complete(obj)
}
}
else
{
print("Http - JsonSerialization Failed, not String:AnyObject Type!!")
}
}
catch let err as NSError
{
print("Http - JsonSerialization Error")
print(err)
}
}
else
{
if error != nil
{
print("Http - URLSession.shared.dataTask Error")
print(error)
}
}
}.resume()
I'm trying to get and return some (json) data from a network call, and I'm wondering if there is a better way to wait for the network data other than just and empty loop.
Here's my code:
func sendAPIRequest(endpoint:String) -> JSON?
{
var json = JSON("")
let request = NSMutableURLRequest(URL: NSURL(string: "https://host.com/".stringByAppendingString(endpoint))!)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
guard error == nil && data != nil else { // check for fundamental networking error
print("error=\(error)")
return
}
if let httpStatus = response as? NSHTTPURLResponse where httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
}
json = JSON(data: data!)
}
task.resume()
while (json == "") {
// Wait
}
return json
}
I remember trying something similar in Objective-C which resulted in an infinite loop at run time.
You need a completion handler if you want to return values from an async operation. dataTaskWithRequest is an async process. Do this:
func sendAPIRequest(endpoint:String, complete: (JSON) -> ())
{
var json = JSON("")
let request = NSMutableURLRequest(URL: NSURL(string: "https://host.com/".stringByAppendingString(endpoint))!)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
guard error == nil && data != nil else { // check for fundamental networking error
print("error=\(error)")
return
}
if let httpStatus = response as? NSHTTPURLResponse where httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
json = JSON(data: data!)
complete(json)
}
}
task.resume()
}
And then call it like so:
sendAPIRequest(<your endpoint string>) { theJSON in
//Use the Json value how u want
}
I have to call a webservice from getResonse(url) then i want to return json . I am unable how to return json in complete handler . They give me error Unexpected non void return value in void function Please help.Any help would be apperciated. Thanks in Advance
func GetStation(url : String) {
var dict = NSDictionary()
dict = getResonse(url)
}
func getResonse(myUrl:NSString) ->NSDictionary
{
let request = NSMutableURLRequest(URL: NSURL(string: myUrl as String)!, cachePolicy: NSURLRequestCachePolicy.ReloadIgnoringCacheData, timeoutInterval: 30)
let session = NSURLSession.sharedSession()
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.HTTPMethod = "GET"
let task = session.dataTaskWithRequest(request) { data, response, error in
guard data != nil else{
return
}
do {
if let json = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary {
print(json)
if (data == nil)
{
return nil
error
}
else
{
return json
error
}
}else {
let jsonStr = NSString(data: data!, encoding: NSUTF8StringEncoding) // No error thrown, but not NSDictionary
print("Error could not parse JSON: \(jsonStr)")
}
} catch let parseError {
print(parseError) // Log the error thrown by `JSONObjectWithData`
let jsonStr = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("Error could not parse JSON: '\(jsonStr)'")
}
}
task.resume()
}
This is merely a coding error (possibly due to braces being all over the place).
In you completion handler, you have 3 forced exits two of which return a value and one that doesn't.
from the "guard data ..." --> else { return }
in the do { ... return nil ... or return json ... }
from the error message I gather that the later two returns are incorrect.
You are inside the dataTaskWithRequest: of NSUrlSession which returns
func dataTaskWithRequest(request: NSURLRequest, completionHandler: (NSData?, NSURLResponse?, NSError?) -> Void) -> NSURLSessionDataTask
It returns Void inside the block. So it's showing an error.