I'm making a POST request to an API and I get the response successfully in Swift. Below is my code.
private func getData(url: NSURL) {
let config: NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
let session: NSURLSession = NSURLSession(configuration: config)
let dataTask: NSURLSessionDataTask = NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: {(data: NSData!, response: NSURLResponse!, error: NSError!) -> Void in
if error {
println("Error Occurred: \(error.localizedDescription)")
} else {
println("\(response.allHeaderFields)") // Error
}
})
dataTask.resume()
}
I'm trying to dump the header fields using allHeaderFields but I get an error saying NSURLResponse does not have a member named allHeaderFields. But it does have it!
There must be something wrong with the syntax or the way I'm calling it. Can anyone please tell me how to correct this?
Thank you.
Elaborating on what Yogesh said...!
Try to cast the NSURLRespones into a NSHTTPURLResponse using "as", because I'm betting the NSURLResponse is actually a NSHTTPURLResponse, or I'm betting that is possible.
Here is what I mean:
private func getData(url: NSURL) {
let config: NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
let session: NSURLSession = NSURLSession(configuration: config)
let dataTask: NSURLSessionDataTask = NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: {(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Void in
if let httpUrlResponse = urlResponse as? NSHTTPURLResponse
{
if error {
println("Error Occurred: \(error.localizedDescription)")
} else {
println("\(httpUrlResponse.allHeaderFields)") // Error
}
}
})
dataTask.resume()
}
From the link you have provided Link
The NSHTTPURLResponse class is a subclass of NSURLResponse that provides methods for accessing information specific to HTTP protocol responses
And allHeaderFields is method of NSHTTPURLResponse class not NSURLResponse class. So you have to use NSHTTPURLResponse instead of NSURLResponse class.
if navigationResponse.response is HTTPURLResponse {
let response = navigationResponse.response as! HTTPURLResponse
print(response.allHeaderFields) // all headers
}
Swift 3 and higher solution
Here is a solution to handle the data tasks in Swift 3 and higher.
let urlPath: String = "http://www.google.de"
guard let url: URL = URL(string: urlPath) else { return }
let request = URLRequest(url: url)
let response: URLResponse?
URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print(error?.localizedDescription ?? "No data")
return
}
if let httpResponse = response as? HTTPURLResponse {
print("error \(httpResponse.statusCode)")
}
}.resume()
Related
Hello I have working json parsing code for swift2.2 but when i use it for Swift 3.0 gives me that error
ViewController.swift:132:31: Ambiguous reference to member 'dataTask(with:completionHandler:)'
My code is here:
let listUrlString = "http://bla.com?batchSize=" + String(batchSize) + "&fromIndex=" + String(fromIndex)
let myUrl = URL(string: listUrlString);
let request = NSMutableURLRequest(url:myUrl!);
request.httpMethod = "GET";
let task = URLSession.shared().dataTask(with: request) {
data, response, error in
if error != nil {
print(error!.localizedDescription)
DispatchQueue.main.sync(execute: {
AWLoader.hide()
})
return
}
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSArray
if let parseJSON = json {
var items = self.categoryList
items.append(contentsOf: parseJSON as! [String])
if self.fromIndex < items.count {
self.categoryList = items
self.fromIndex = items.count
DispatchQueue.main.async(execute: {
self.categoriesTableView.reloadData()
AWLoader.hide()
})
}else if( self.fromIndex == items.count){
DispatchQueue.main.async(execute: {
AWLoader.hide()
})
}
}
} catch {
AWLoader.hide()
print(error)
}
}
task.resume()
Thanks for ideas.
The compiler is confused by the function signature. You can fix it like this:
let task = URLSession.shared.dataTask(with: request as URLRequest) {
But, note that we don't have to cast "request" as URLRequest in this signature if it was declared earlier as URLRequest instead of NSMutableURLRequest:
var request = URLRequest(url:myUrl!)
This is the automatic casting between NSMutableURLRequest and the new URLRequest that is failing and which forced us to do this casting here.
You have init'd myRequest as NSMutableURLRequest, you need this:
var URLRequest
Swift is ditching both the NSMutable... thing. Just use var for the new classes.
Xcode 8 and Swift 3.0
Using URLSession:
let url = URL(string:"Download URL")!
let req = NSMutableURLRequest(url:url)
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue.main)
let task : URLSessionDownloadTask = session.downloadTask(with: req as URLRequest)
task.resume()
URLSession Delegate call:
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask,
didWriteData bytesWritten: Int64, totalBytesWritten writ: Int64, totalBytesExpectedToWrite exp: Int64) {
print("downloaded \(100*writ/exp)" as AnyObject)
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL){
}
Using Block GET/POST/PUT/DELETE:
let request = NSMutableURLRequest(url: URL(string: "Your API URL here" ,param: param))!,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval:"Your request timeout time in Seconds")
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers as? [String : String]
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest) {data,response,error in
let httpResponse = response as? HTTPURLResponse
if (error != nil) {
print(error)
} else {
print(httpResponse)
}
DispatchQueue.main.async {
//Update your UI here
}
}
dataTask.resume()
Working fine for me.. try it 100% result guarantee
This problem is caused by URLSession has two dataTask methods
open func dataTask(with request: URLRequest, completionHandler: #escaping (Data?, URLResponse?, Error?) -> Swift.Void) -> URLSessionDataTask
open func dataTask(with url: URL, completionHandler: #escaping (Data?, URLResponse?, Error?) -> Swift.Void) -> URLSessionDataTask
The first one has URLRequest as parameter, and the second one has URL as parameter, so we need to specify which type to call, for example, I want to call the second method
let task = URLSession.shared.dataTask(with: url! as URL) {
data, response, error in
// Handler
}
In my case error was in NSURL
let url = NSURL(string: urlString)
In Swift 3 you must write just URL:
let url = URL(string: urlString)
Tested xcode 8 stable version ; Need to use var request variable with URLRequest() With thats you can easily fix that (bug)
var request = URLRequest(url:myUrl!) And
let task = URLSession.shared().dataTask(with: request as URLRequest) { }
Worked fine ! Thank you guys, i think help many people. !
For Swift 3 and Xcode 8:
var dataTask: URLSessionDataTask?
if let url = URL(string: urlString) {
self.dataTask = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
if let error = error {
print(error.localizedDescription)
} else if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 {
// You can use data received.
self.process(data: data as Data?)
}
})
}
}
//Note: You can always use debugger to check error
In swift 3 the compiler is confused by the function signature. Specifying it will clear the error. Also convert the url string to type URL. The following code worked for me.
let urlString = "http://bla.com?batchSize="
let pathURL = URL(string: urlString)!
var urlRequest = URLRequest(url:pathURL)
let session = URLSession.shared
let dataTask = session.dataTask(with: urlRequest as URLRequest) { (data,response,error) in
Short and concise answer for Swift 3:
guard let requestUrl = URL(string: yourURL) else { return }
let request = URLRequest(url:requestUrl)
URLSession.shared.dataTask(with: request) {
(data, response, error) in
...
}.resume()
// prepare json data
let mapDict = [ "1":"First", "2":"Second"]
let json = [ "title":"ABC" , "dict": mapDict ] as [String : Any]
let jsonData : NSData = NSKeyedArchiver.archivedData(withRootObject: json) as NSData
// create post request
let url = NSURL(string: "http://httpbin.org/post")!
let request = NSMutableURLRequest(url: url as URL)
request.httpMethod = "POST"
// insert json data to the request
request.httpBody = jsonData as Data
let task = URLSession.shared.dataTask(with: request as URLRequest){ data,response,error in
if error != nil{
return
}
do {
let result = try JSONSerialization.jsonObject(with: data!, options: []) as? [String:AnyObject]
print("Result",result!)
} catch {
print("Error -> \(error)")
}
}
task.resume()
To load data via a GET request you don't need any URLRequest (and no semicolons)
let listUrlString = "http://bla.com?batchSize=" + String(batchSize) + "&fromIndex=" + String(fromIndex)
let myUrl = URL(string: listUrlString)!
let task = URLSession.shared.dataTask(with: myUrl) { ...
let task = URLSession.shared.dataTask(with: request as URLRequest, completionHandler: { data,response,error in
if error != nil{
print(error!.localizedDescription)
return
}
if let responseJSON = (try? JSONSerialization.jsonObject(with: data!, options: [])) as? [String:AnyObject]{
if let response_token:String = responseJSON["token"] as? String {
print("Singleton Firebase Token : \(response_token)")
completion(response_token)
}
}
})
task.resume()
Xcode 10.1 Swift 4
This worked for me:
let task: URLSessionDataTask = session.dataTask(with: request as URLRequest) { (data, response, error) -> Void in
...
The key was adding in the URLSessionDataTask type declaration.
For me I do this to find,
let url = URL(string: urlString)
URLSession.shared.dataTask(with: url!) { (data, response, error) in ...}
Can't use
"let url = NSURL(string: urlString)
I just upgraded to Swift 2.0 and it looks like there are issues with my sendRequest function.
Swift 1.2 Code (old code):
func sendRequest(request: NSURLRequest, completion:(NSData!, NSError!) -> Void) -> () {
// Create a NSURLSession task
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) { (data: NSData!, response: NSURLResponse!, error: NSError!) in
if error != nil {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
completion(data, error)
})
return
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
if let httpResponse = response as? NSHTTPURLResponse {
if httpResponse.statusCode == 200 {
completion(data, nil)
} else {
var jsonerror:NSError?
if let errorDict = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments, error:&jsonerror) as? NSDictionary {
let responseError : NSError = NSError(domain: "HTTPHelperError", code: httpResponse.statusCode, userInfo: errorDict as? [NSObject : AnyObject])
completion(data, responseError)
}
}
}
})
}
// start the task
task.resume()
}
I made changes for Swift 2.0 and managed to compile without errors. But the data keeps returning Error Code 400 (bad request).
Current code attempt (Swift 2.0):
This is the current code (compiles, but returns error 400)
func sendRequest(request: NSURLRequest, completion:(NSData!, NSError!) -> Void) -> () {
// Create a NSURLSession task
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) { (data, response, error) in
if error != nil {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
completion(data, error)
})
return
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
if let httpResponse = response as? NSHTTPURLResponse {
if httpResponse.statusCode == 200 {
completion(data, nil)
} else {
do {
if let errorDict = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.AllowFragments) as? NSDictionary
{
let responseError : NSError = NSError(domain: "HTTPHelperError", code: httpResponse.statusCode, userInfo: errorDict as? [NSObject : AnyObject])
completion(data, responseError)
}
} catch let error as NSError {
print("error serializing JSON: \(error)")
completion(data, error)
}
}
}
})
}
// start the task
task.resume()
}
Source: I was following this tutorial but looks like it was written for Swift 1.2
Edit: This is the buildRequest function
func buildRequest(path: String!, method: String, authType: HTTPRequestAuthType,
requestContentType: HTTPRequestContentType = HTTPRequestContentType.HTTPJsonContent, requestBoundary:String = "") -> NSMutableURLRequest {
// 1. Create the request URL from path
let requestURL = NSURL(string: "\(HTTPHelper.BASE_URL)/\(path)")
let request = NSMutableURLRequest(URL: requestURL!)
// Set HTTP request method and Content-Type
request.HTTPMethod = method
// 2. Set the correct Content-Type for the HTTP Request. This will be multipart/form-data for photo upload request and application/json for other requests in this app
switch requestContentType {
case .HTTPJsonContent:
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
case .HTTPMultipartContent:
let contentType = "multipart/form-data; boundary=\(requestBoundary)"
request.addValue(contentType, forHTTPHeaderField: "Content-Type")
}
// 3. Set the correct Authorization header.
switch authType {
case .HTTPBasicAuth:
// Set BASIC authentication header
let basicAuthString = "\(HTTPHelper.API_AUTH_NAME):\(HTTPHelper.API_AUTH_PASSWORD)"
let utf8str = basicAuthString.dataUsingEncoding(NSUTF8StringEncoding)
let base64EncodedString = utf8str?.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
request.addValue("Basic \(base64EncodedString!)", forHTTPHeaderField: "Authorization")
case .HTTPTokenAuth:
// Retrieve Auth_Token from Keychain
if let userToken = KeychainAccess.passwordForAccount("Auth_Token", service: "KeyChainService") as String? {
// Set Authorization header
request.addValue("Token token=\(userToken)", forHTTPHeaderField: "Authorization")
}
}
return request
}
Try This one :-
func sendRequest(request: NSURLRequest, completion:(NSData!, NSError!) -> Void) -> () {
let request = request
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
let error = error
let response = response
let data = data
if data != nil && error == nil {
let res = response as! NSHTTPURLResponse!
if (res.statusCode >= 200 && res.statusCode < 300) {
do {
let jsonData:AnyObject? = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as? NSDictionary
print("Success")
} catch let error as NSError {
print("Failed To load")
} catch {
// Something else happened.
// Insert your domain, code, etc. when constructing the error.
}
} else {
print("Connection Error")
}
} else {
print("Error")
}
})
task.resume()
}
and also in your buildRequest, use set :-
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
If you're on Swift 2 it's worth double-checking that your info.plist has an appropriate value for App Transport Security Settings ...
instead of saying "if let errorDict... { }
remove the "if" statement and take away the brackets. So it looks like :
do {
let errorDict = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.AllowFragments) as? NSDictionary
let responseError : NSError = NSError(domain: "HTTPHelperError", code: httpResponse.statusCode, userInfo: errorDict as? [NSObject : AnyObject])
completion(data, responseError)
}
catch {
//catch code here
}
I want to implement the connection retry in NSURLSession. Is there any parameter we need to set to achieve this like 'timeoutIntervalForRequest' and NSURLSession takes the responsibility to retry the connection.
If there is no any parameter for this, how can we achieve this?
My current code is as follows:
func isHostConnected(jsonString:NSDictionary) -> NSDictionary
{
let request = NSMutableURLRequest(URL: NSURL(string: "http://***.*.*.**:****/")!)
do {
request.HTTPBody = try NSJSONSerialization.dataWithJSONObject(jsonString, options: [])
} catch {
//error = error1
request.HTTPBody = nil
}
request.timeoutInterval = 4.0 //(number as! NSTimeInterval)
request.HTTPMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("gzip", forHTTPHeaderField: "Accept-encoding")
var JSONdata: AnyObject = ["" : ""] as Dictionary<String, String>
print(JSONdata)
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
var responseCode = -1
let group = dispatch_group_create()
dispatch_group_enter(group)
session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
if let httpResponse = response as? NSHTTPURLResponse {
responseCode = httpResponse.statusCode
let JSONresdata: AnyObject = (try! NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers))
JSONdata = JSONresdata as! NSDictionary
}
dispatch_group_leave(group)
}).resume()
dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
print("responseCode == 200: \(responseCode)")
return (JSONdata) as! NSDictionary
}
When response code is not 200 then this function should retry the connection again. Can I do the same.
Please check the answer of this link
func someMethodWithRetryCounter(retryCounter: Int) {
if retryCounter == 0 {
return
}
retryCounter--
var request: NSMutableURLRequest = NSMutableURLRequest.requestWithURL(NSURL.URLWithString(self.baseUrl.stringByAppendingString(path)))
(self) weakSelf = self
var dataTask: NSURLSessionDataTask = NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: {(data: NSData, response: NSURLResponse, error: NSErrorPointer) in var httpResponse: NSHTTPURLResponse = response
var responseStatusCode: UInt = httpResponse.statusCode()
if responseStatusCode != 200 {
weakSelf.someMethodWithRetryCounter(retryCounter)
}
else {
completionBlock(results["result"][symbol])
}
})
dataTask.resume()
}
You can also use the following default iOS function. It provide a replacement request body stream if the task needs to resend a request that has a body stream because of an authentication challenge or other recoverable server error.
Check these Link/Link for reference
func URLSession(_ session: NSURLSession,
task task: NSURLSessionTask,
needNewBodyStream completionHandler: (NSInputStream?) -> Void)
Hope this might be helpful.
I have an application that retrieves JSON data from my server and enters it in to my Core Data model. I have this working reasonably well, minus a few issues with random EXC_BAD_ACCESS errors that I haven't figured out yet. During this dataTask, it can retrieve large amounts of data so it can take a while to complete.
I'm looking for a way to let the user suspend the app the dataTask will continue to retrieve the data. I seen NSURLSession has a background mode, but seen it only supports upload and download tasks.
Is there a way to support this?
My dataTask function:
class func Request(file: String, withData: String?, completion: (NSData -> Void)) {
let url = NSURL(string: "\(root)\(file)")!
let request = NSMutableURLRequest(URL: url)
if let sentData = withData {
request.HTTPMethod = "POST"
request.HTTPBody = sentData.dataUsingEncoding(NSUTF8StringEncoding)
}
let dataTask = session.dataTaskWithRequest(request) {
data, response, error in
if error != nil {
if error?.domain == NSURLErrorDomain && error?.code == NSURLErrorTimedOut {
print("Data task timed out")
}
} else {
let httpResponse : NSHTTPURLResponse = response as! NSHTTPURLResponse
if httpResponse.statusCode == 200 {
completion(data!)
} else {
print("Request failed with status code: \(httpResponse.statusCode)")
}
}
}
dataTask.resume()
}
import Foundation
class NetworkOperation {
lazy var config: NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
lazy var session: NSURLSession = NSURLSession(configuration: self.config)
let queryURL: NSURL
typealias JSONDictionaryCompletion = ([String: AnyObject]? -> Void)
init(url: NSURL) {
self.queryURL = url
}
func downloadJSONFromURL(completion: JSONDictionaryCompletion) {
let request = NSURLRequest(URL: queryURL)
let dataTask = session.dataTaskWithRequest(request) {
(let data, let response, let error) in
// 1. Check HTTP response for successful GET request
if let httpResponse = response as? NSHTTPURLResponse {
switch httpResponse.statusCode {
case 200:
// 2. Create JSON object with data
let jsonDictionary = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: nil)
completion(jsonDictionary)
default:
print("GET request not successful. HTTP status code: \(httpResponse.statusCode)")
}
} else {
print("Error: Not a valid HTTP response")
}
}
dataTask.resume()
}
}
In the 'Create JSON object with data' step, I keep receiving the "extra argument 'error' in call". What is happening? I am unable to find documentation to help me further in this.
You can do it by this way.
do{
var jsonDictionary = try NSJSONSerialization.JSONObjectWithData(data, options:NSJSONReadingOptions.MutableContainers)
//completion(jsonDictionary)
}catch{
// report error
}
at the top of step 2: creating json....
add this line:
var err: NSError?
// 1. Check HTTP response for successful GET request
if let httpResponse = response as? NSHTTPURLResponse {
switch httpResponse.statusCode {
case 200:
// 2. Create JSON object with data
let jsonDictionary = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: nil) as? [String: AnyObject]
completion(jsonDictionary)
default:
println("GET request not successful. HTTP status code: \(httpResponse.statusCode)")
}
} else {
println("Error: Not a valid HTTP response")
}
}
dataTask.resume()
}
}
Finally figured it out! Thank you for your input everyone!