I'm facing a problem while requesting from server, everything was fine
suddenly the Alamofire.request can't access responseJSON and can't access the body of the function and it returns , what I have to do?
I'm using multi Alamofire request in the navigationController but after two or three requests this happens!
I'm really in trouble please help me!
this code can't execute
let group = DispatchGroup()
group.enter()
Alamofire.request(url, method: .get, parameters: nil,
encoding:
JSONEncoding.default, headers: header as? HTTPHeaders)
.responseJSON { (response) in
if response.result.isSuccess{
if let response = response.result.value {
let json = JSON(response)
self.updateAnswers(json: json)
print(json.arrayValue)
group.leave()
}
}
}
If it’s not successful, you should dive in and take a look at the error that was returned. You might even want to look at the body of the response (if its a development server, it might provide some useful error messages in the body of the response).
let group = DispatchGroup()
group.enter()
Alamofire.request(...)
.responseJSON { response in
defer { group.leave() }
switch response.result {
case .failure(let error):
print(error)
print(response.response ?? "no HTTPURLResponse")
if let data = response.data {
if let string = String(data: data, encoding: .utf8) {
print(string)
} else {
print(data as NSData)
}
} else {
print("no data")
}
case .success(let json):
print(json)
}
}
By the way, I’d suggest moving the leave call outside of any if/switch statement because you want to make sure your dispatch group is satisfied regardless of whether it was successful or not.
But, bottom line, you won’t be able to diagnose the problem until you start to look at precisely what error was returned.
Related
I'm performing a post request over my rest api that I built in node. Then the data are stored in a mongodb collection.
This is my code that I use to post the request:
// create dataTask using the session object to send data to the server
let task = session.dataTask(with: request) { data, response, error in
if let error = error {
print("Post Request Error: \(error.localizedDescription)")
return
}
// ensure there is valid response code returned from this HTTP response
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode)
else {
print("Invalid Response received from the server")
return
}
// ensure there is data returned
guard let responseData = data else {
print("nil Data received from the server")
return
}
do {
// create json object from data or use JSONDecoder to convert to Model stuct
if let jsonResponse = try JSONSerialization.jsonObject(with: responseData, options: .mutableContainers) as? [String: ErrorHandler] {
print(jsonResponse)
DispatchQueue.main.async {
self?.isLoading = false
self?.signedIn = true
}
} else {
print("data maybe corrupted or in wrong format")
throw URLError(.badServerResponse)
}
} catch let error {
print(error.localizedDescription)
}
}
task.resume()
The problem is that the responseJson is always nil. I tried to perform the same request with postman and I get the response correctly. What is the problem? Also because the data are correctly uploaded everywhere.
This is my postman result of the same post request.
I am obviously missing something very fundamental/naïve/etc., but for the life of me I cannot figure out how to make simple GET requests.
I'm trying to make an HTTP GET request with Swift 5. I've looked at these posts/articles: one, two, but I can't get print() statements to show anything. When I use breakpoints to debug, the entire section within the URLSession.shared.dataTask section is skipped.
I am looking at the following code (from the first link, above):
func HTTP_Request() {
let url = URL(string: "http://www.stackoverflow.com")!
let task = URLSession.shared.dataTask(with: url) {(data: Data?, response: URLResponse?, error: Error?) in
guard let data = data else { return }
print(String(data: data, encoding: .utf8)!)
}
task.resume()
}
HTTP_Request()
I am running this in a MacOS Command Line Project created through XCode.
I would greatly appreciate any help I can get on this, thank you.
Right now, if there is an error, you are going to silently fail. So add some error logging, e.g.,
func httpRequest() {
let url = URL(string: "https://www.stackoverflow.com")! // note, https, not http
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard
error == nil,
let data = data,
let string = String(data: data, encoding: .utf8)
else {
print(error ?? "Unknown error")
return
}
print(string)
}
task.resume()
}
That should at least give you some indication of the problem.
A few other considerations:
If command line app, you have to recognize that the app may quit before this asynchronous network request finishes. One would generally start up a RunLoop, looping with run(mode:before:) until the network request finishes, as advised in the run documentation.
For example, you might give that routine a completion handler that will be called on the main thread when it is done. Then you can use that:
func httpRequest(completion: #escaping () -> Void) {
let url = URL(string: "https://www.stackoverflow.com")! // note, https, not http
let task = URLSession.shared.dataTask(with: url) { data, response, error in
defer {
DispatchQueue.main.async {
completion()
}
}
guard
error == nil,
let data = data,
let string = String(data: data, encoding: .utf8)
else {
print(error ?? "Unknown error")
return
}
print(string)
}
task.resume()
}
var finished = false
httpRequest {
finished = true
}
while !finished {
RunLoop.current.run(mode: .default, before: .distantFuture)
}
In standard macOS apps, you have to enable outgoing (client) connections in the “App Sandbox” capabilities.
If playground, you have to set needsIndefiniteExecution.
By default, macOS and iOS apps disable http requests unless you enable "Allow Arbitrary Loads” in your Info.plist. That is not applicable to command line apps, but you should be aware of that should you try to do this in standard macOS/iOS apps.
In this case, you should just use https and avoid that consideration altogether.
Make sure the response get print before exiting the process, you could try to append
RunLoop.main.run()
or
sleep(UINT32_MAX)
in the end to make sure the main thread won't exit. If you want to print the response and exit the process immediately, suggest using DispatchSemaphore:
let semphare = DispatchSemaphore(value: 0)
func HTTP_Request() {
let url = URL(string: "http://www.stackoverflow.com")!
let task = URLSession.shared.dataTask(with: url) {(data: Data?, response: URLResponse?, error: Error?) in
guard let data = data else { return }
print(String(data: data, encoding: .utf8)!)
semphare.signal()
}
task.resume()
}
HTTP_Request()
_ = semphare.wait(timeout: .distantFuture)
This works for me many times I suggest you snippet for future uses!
let url = URL(string: "https://google.com")
let task = URLSession.shared.dataTask(with: ((url ?? URL(string: "https://google.com"))!)) { [self] (data, response, error) in
do {
let jsonResponse = try JSONSerialization.jsonObject(with: data!, options: [])
print(jsonResponse)
guard let newValue = jsonResponse as? [String:Any] else {
print("invalid format")
}
}
catch let error {
print("Error: \(error)")
}
task.resume()
}
Alamofire.request(NEWS_FEED_URL).responseJSON { response in
guard let newsResponse = response.result.value as? [[String:String]] else{
print("Error")
return
}
print("JSON: \(newsResponse)")
This is my alamofire code to get response from server. Most of the time it is working fine but sometime it goes failure and print Error. Even if I paste print("JSON:", newsResponse) in failure block it shows the response but it is not going to the success block. I also print the status code once. Its giving me 200. My Internet is working good, the related url is giving response in postman. But sometimes it is not working why?
Try this method
func apiRequest(method:String, urlMethod:String, parametersDictionary:NSMutableDictionary, success:#escaping successDictionaryBlock, failure: #escaping failBlockErrorMessage){
let requestUrl = "Request URL"
Alamofire.request(requestUrl, method: .post, parameters: (parametersDictionary as NSDictionary) as? Parameters , encoding: JSONEncoding.default, headers: nil).responseJSON { response in
print(response)
print(requestUrl)
if(response.result.error == nil){
if((response.response?.statusCode)! < 500){
if(response.response?.statusCode == 200){
if let JSON = response.result.value {
print(JSON)
let dict = JSON as! NSDictionary
let status :Bool = dict["status"] as! Bool
if(status){
success(dict)
}else{
failure(dict["message"] as! String)
}
}
}else{
failure("Something went wrong please try again")
}
}else{
failure("Something went wrong please try again")
}
}
}
}
I am building an swift app to interact with an MDM API to do large numbers of updates via PUT commands, and I am running in to issues with how to handle the massive numbers of API calls without overloading the servers.
I am parsing through a CSV, and each line is an update. If I run the commands asynchronously, it generates and sends ALL of the API calls immediately, which the server doesn't like.
But if I run the commands synchronously, it freezes my GUI which is less than ideal, as the end user doesn't know what's going on, how long is left, if things are failing, etc.
I have also tried creating my own NSOperation queue and setting the max number of items to like 5, and then putting the synchronous function in there, but that doesn't seem to work very well either. It still freezes the GUI with some really random UI updates that seem buggy at best.
The servers can handle 5-10 requests at a time, but these CSV files can be upwards of 5,000 lines sometimes.
So how can I limit the number of simultaneous PUT requests going out in my loop, while not having the GUI freeze on me? To be honest, I don't even really care if the end user can interact with the GUI while it's running, I just want to be able to provide feedback on the lines that have run so far.
I have a wrapper which a colleague wrote most of, and the async function looks like this:
func sendRequest(endpoint: String, method: HTTPMethod, base64credentials: String, dataType: DataType, body: Data?, queue: DispatchQueue, handler: #escaping (Response)->Swift.Void) {
let url = self.resourceURL.appendingPathComponent(endpoint)
var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 30.0)
request.httpMethod = "\(method)"
var headers = ["Authorization": "Basic \(base64credentials)"]
switch dataType {
case .json:
headers["Content-Type"] = "application/json"
headers["Accept"] = "application/json"
if let obj = body {
do {
request.httpBody = try JSONSerialization.data(withJSONObject: obj, options: JSONSerialization.WritingOptions(rawValue: 0))
} catch {
queue.async {
handler(.badRequest)
}
return
}
}
case .xml:
headers["Content-Type"] = "application/xml"
headers["Accept"] = "application/xml"
request.httpBody = body
/*if let obj = body {
request.httpBody = (obj as! XMLDocument).xmlData
}*/
}
request.allHTTPHeaderFields = headers
session.dataTask(with: request) {
var response: Response
if let error = $2 {
response = .error(error)
} else {
let httpResponse = $1 as! HTTPURLResponse
switch httpResponse.statusCode {
case 200..<299:
if let object = try? JSONSerialization.jsonObject(with: $0!, options: JSONSerialization.ReadingOptions(rawValue: 0)) {
response = .json(object)
} else if let object = try? XMLDocument(data: $0!, options: 0) {
response = .xml(object)
} else {
response = .success
}
default:
response = .httpCode(httpResponse.statusCode)
}
}
queue.async {
handler(response)
}
}.resume()
Then, there is a synchronous option which uses semaphore, which looks like this:
func sendRequestAndWait(endpoint: String, method: HTTPMethod, base64credentials: String, dataType: DataType, body: Data?) -> Response {
var response: Response!
let semephore = DispatchSemaphore(value: 0)
sendRequest(endpoint: endpoint, method: method, base64credentials: base64credentials, dataType: dataType, body: body, queue: DispatchQueue.global(qos: .default)) {
response = $0
semephore.signal()
}
semephore.wait()
return response
}
Usage information is as follows:
class ViewController: NSViewController {
let client = JSSClient(urlString: "https://my.mdm.server:8443/", allowUntrusted: true)
let credentials = JSSClient.Credentials(username: "admin", password: "ObviouslyNotReal")
func asynchronousRequestExample() {
print("Sending asynchronous request")
client.sendRequest(endpoint: "computers", method: .get, credentials: credentials, dataType: .xml, body: nil, queue: DispatchQueue.main) { (response) in
print("Response recieved")
switch response {
case .badRequest:
print("Bad request")
case .error(let error):
print("Receieved error:\n\(error)")
case .httpCode(let code):
print("Request failed with http status code \(code)")
case .json(let json):
print("Received JSON response:\n\(json)")
case .success:
print("Success with empty response")
case .xml(let xml):
print("Received XML response:\n\(xml.xmlString(withOptions: Int(XMLNode.Options.nodePrettyPrint.rawValue)))")
}
print("Completed")
}
print("Request sent")
}
func synchronousRequestExample() {
print("Sending synchronous request")
let response = client.sendRequestAndWait(endpoint: "computers", method: .get,credentials: credentials, dataType: .json, body: nil)
print("Response recieved")
switch response {
case .badRequest:
print("Bad request")
case .error(let error):
print("Receieved error:\n\(error)")
case .httpCode(let code):
print("Request failed with http status code \(code)")
case .json(let json):
print("Received JSON response:\n\(json)")
case .success:
print("Success with empty response")
case .xml(let xml):
print("Received XML response:\n\(xml.xmlString(withOptions: Int(XMLNode.Options.nodePrettyPrint.rawValue)))")
}
print("Completed")
}
override func viewDidAppear() {
super.viewDidAppear()
synchronousRequestExample()
asynchronousRequestExample()
}
I have modified the send functions slightly, so that they take base64 encoded credentials off the bat, and maybe one or two other things.
Can't you just chain operations to send 3/4 requests at a time per operation?
https://www.raywenderlich.com/76341/use-nsoperation-nsoperationqueue-swift
Just so you know, NSOperation (also abstracted by Operation with Swift3) are running by default on background threads. Just be careful to not run heavy tasks in your completion block that might run tasks on the main thread (this will freeze your UI).
The only other case I see that can freeze your UI is by executing too many operations at once.
Well, I think I got this covered! I decided to climb out of the rabbit hole a ways and simplify things. I wrote my own session instead of relying on the wrapper, and set up semaphores in it, threw it in an OperationQueue and it seems to be working perfectly.
This was the video I followed to set up my simplified semaphores request. https://www.youtube.com/watch?v=j4k8sN8WdaM
I'll have to tweak the below code to be a PUT instead of the GET I've been using for testing, but that part is easy.
//print (row[0])
let myOpQueue = OperationQueue()
myOpQueue.maxConcurrentOperationCount = 3
let semaphore = DispatchSemaphore(value: 0)
var i = 0
while i < 10 {
let myURL = NSURL(string: "https://my.server.com/APIResources/computers/id/\(i)")
myOpQueue.addOperation {
let request = NSMutableURLRequest(url: myURL! as URL)
request.httpMethod = "GET"
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = ["Authorization" : "Basic 123456789ABCDEFG=", "Content-Type" : "text/xml", "Accept" : "text/xml"]
let session = Foundation.URLSession(configuration: configuration)
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(data, response, error) -> Void in
if let httpResponse = response as? HTTPURLResponse {
print(httpResponse.statusCode)
semaphore.signal()
self.lblLine.stringValue = "\(i)"
self.appendLogString(stringToAppend: "\(httpResponse.statusCode)")
print(myURL!)
}
if error == nil {
print("No Errors")
print("")
} else {
print(error!)
}
})
task.resume()
semaphore.wait()
}
i += 1
}
I have a very simple http request which will return a JSON data. Here is my code:
let query = NSString(format: "http://glosbe.com/gapi/translate?from=eng&dest=eng&format=json&phrase=test",src, dest, phrase )
let url = NSURL(string: query)
let task = NSURLSession.sharedSession().dataTaskWithURL(url) {(data, response, error) in
if let httpRes = response as? NSHTTPURLResponse {
println("status code=",httpRes.statusCode)
if httpRes.statusCode == 200 {
println(NSString(data: data, encoding: NSUTF8StringEncoding))
// parse data
let phrase = Phrase.parse(data)
println(phrase.description)
}
}
}
task.resume()
Sometimes the completionHandler isn't called at all. I suspect it's the problem of the server. But when I input the same url into my browser and tried a dozens times. There was no problem at all. The data was returned everything when I refresh the browser.
Is there anything wrong in my code? Thanks
The code works OK for me. I suggest you can make this change to your code (an else clause):
if let httpRes = response as? NSHTTPURLResponse {
println("status code=",httpRes.statusCode)
if httpRes.statusCode == 200 {
println(NSString(data: data, encoding: NSUTF8StringEncoding))
// parse data
let phrase = Phrase.parse(data)
println(phrase.description)
}
} else {
println("error \(error)") // print the error!
}
So you will have a better idea if anything goes wrong