public func dataTaskWithRequest(request: NSURLRequest, completionHandler: (NSData?, NSURLResponse?, NSError?) -> Void) -> NSURLSessionDataTask
I know the usage method of above.
public func dataTaskWithRequest(request: NSURLRequest) -> NSURLSessionDataTask
But what is the usage of this method? I'm confused about how it is used.All examples I see are using the first method. The method returns no data, no error to handle, nor any response? Or is that you somehow envelope this as a task and later run in a queue?
You use this latter method if you've specified a delegate for a custom NSURLSession. The data is not returned to the closure, but rather the session calls your delegate's didReceiveData, which you have to implement separately.
It takes more work to implement the delegate methods, though, so you generally only do that where you absolutely have to (e.g. you want to process data as it comes in rather than waiting for all of the data to come in; you need delegate methods for custom handling of redirects and challenges; you are doing background NSURLSession with download or upload tasks rather than data tasks; etc.).
For example, to issue a simple GET request, expecting JSON response, you might define your class to conform to URLSessionDelegate, URLSessionDataDelegate, and, optionally, URLSessionTaskDelegate (called NSURLSessionDelegate, NSURLSessionDataDelegate and NSURLSessionTaskDelegate, respectively, in Objective-C and Swift 2), and then do something like the following in Swift 3:
var responseData: Data?
func performRequest() {
let url = URL(string: "http://ip.jsontest.com/")!
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Accept")
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
responseData = Data()
let task = session.dataTask(with: request)
task.resume()
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
responseData!.append(data)
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
guard error == nil else {
print(error!)
return
}
do {
guard let jsonObject = try JSONSerialization.jsonObject(with: responseData!) as? [String: AnyObject] else {
print("response was not JSON dictionary")
print("responseString: \(String(data: responseData!, encoding: .utf8))")
return
}
print(jsonObject)
} catch let parseError {
print("JSON Error: \(parseError)")
}
}
Or, in Swift 2:
var responseData: NSMutableData?
func performRequest() {
let url = NSURL(string: "http://ip.jsontest.com/")!
let request = NSMutableURLRequest(URL: url)
request.setValue("application/json", forHTTPHeaderField: "Accept")
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: configuration, delegate: self, delegateQueue: nil)
responseData = NSMutableData()
let task = session.dataTaskWithRequest(request)
task.resume()
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
responseData!.appendData(data)
}
func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
guard error == nil else {
print(error!)
return
}
do {
guard let jsonObject = try NSJSONSerialization.JSONObjectWithData(responseData!, options: []) as? [String: AnyObject] else {
print("response was not JSON dictionary")
print("responseString: \(String(data: responseData!, encoding: NSUTF8StringEncoding))")
return
}
print(jsonObject)
} catch let parseError {
print("JSON Error: \(parseError)")
}
}
Clearly, you wouldn't do this unless you needed some of the more complicated delegate methods, too, but I wanted to show a minimalist implementation (which helps us appreciate the completion handler rendition).
public func dataTaskWithRequest(request: NSURLRequest, completionHandler: (NSData?, NSURLResponse?, NSError?) -> Void) -> NSURLSessionDataTask
returns data, response and error directly in a completion handler.
public func dataTaskWithRequest(request: NSURLRequest) -> NSURLSessionDataTask
is used in conjunction with the URLSessionDataDelegate protocol. A couple of methods are required to be implemented. During the several stages the delegate methods are called to process the data.
The protocol method provides finer control of the loading process, for example to handle credential requests and redirection control.
Related
I can't solve this error I hope can I find the solution here
the error is in the line 22 and it's "Cannot convert value of type () to expected argument type (Data?, URLResponse?, Error?) -> Void"
struct WeatherManager {
let weatherURL = ""
func fetchWeather(cityName : String){
let urlString = "\(weatherURL)&q=\(cityName)"
performRequest(urlString: urlString)
}
func performRequest(urlString : String){
//1- Create a URL
if let url = URL(string: urlString){
//2- Create a URLSession
let session = URLSession(configuration: .default)
//3- Give the session a task
let task = session.dataTask(with: url, completionHandler:handel(data: <#T##Data?#>, urlSession: <#T##URLSession?#>, error: <#T##Error?#>))
//4- Start the Task
task.resume()
}
}
func handel(data:Data? , urlSession : URLSession? , error : Error?){
if error != nil {
print(error!)
return
}
if let safeData = data{
let dataString = String(data: safeData, encoding: .utf8)
print(dataString!)
}
}
}
here is the error :
let task = session.dataTask(with: url, completionHandler:handel(data: <#T##Data?#>, urlSession: <#T##URLSession?#>, error: <#T##Error?#>))
A few observations:
The signature of “handle” is incorrect. The second parameter is a URLResponse, not a URLSession:
func handle(data: Data?, response: URLResponse?, error: Error?) {
if let error = error {
print(error)
return
}
if let data = data, let string = String(data: data, encoding: .utf8) {
print(string)
}
}
As Leo pointed out, you should avoid force-unwrapping with the ! operator, too (especially when converting the Data to a UTF8 String).
You should not supply parameters when you pass handle to dataTask:
func performRequest(urlString: String) {
//1- Create a URL
guard let url = URL(string: urlString) else { return }
//2- Use existing URLSession
let session = URLSession.shared // (configuration: .default)
//3- Give the session a task
let task = session.dataTask(with: url, completionHandler: handle)
//4- Start the Task
task.resume()
}
Unrelated, please note that I have not created a URLSession in performRequest. Sessions will leak memory if you create them but do not invalidate them. In this case, it is easier to use the existing shared session, rather than creating a new one.
If you really needed to create one (which you obviously do not in this case ... use shared instead), you would explicitly tell it to invalidate itself when the request was done.
func performRequest(urlString: String) {
//1- Create a URL
guard let url = URL(string: urlString) else { return }
//2- Create URLSession (but only if absolutely necessary)
let session = URLSession(configuration: .default)
//3- Give the session a task
let task = session.dataTask(with: url, completionHandler: handle)
//4- Start the Task
task.resume()
//5- If you really must create a session, invalidate it when the request(s) are finished
session.finishTasksAndInvalidate()
}
But this is an anti-pattern because it is so inefficient. You should reuse a single session, like shown in the previous code snippet, if at all possible.
I have implemented a background upload task using URLSession as follow:
let boundary = UUID().uuidString
let configuration = URLSessionConfiguration.background(withIdentifier: "com.example.upload")
let session = URLSession(configuration: configuration, delegate: self, delegateQueue: .main)
var urlRequest = URLRequest(url: URL(string: "https://server/file.php")!)
urlRequest.httpMethod = "POST"
urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
var data = Data()
data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
data.append("Content-Disposition: form-data; name=\"video\"; filename=\"rawvideo.mp4\"\r\n".data(using: .utf8)!)
data.append("Content-Type: video/mp4\r\n\r\n".data(using: .utf8)!)
data.append(videoData!)
data.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!)
let tempDir = FileManager.default.temporaryDirectory
let localURL = tempDir.appendingPathComponent("upload")
try? data.write(to: localURL)
session.uploadTask(with: urlRequest, fromFile: localURL).resume()
To handle the server response, I have used the following code [But I am not getting any print output. Seems this method is never called]
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: #escaping (URLSession.ResponseDisposition) -> Void) {
print("anoop")
guard let response = response as? HTTPURLResponse, (200...209).contains(response.statusCode) else {
DispatchQueue.main.async {
self.uploadComplete("Error", "Upload Server Down!")
}
print("error")
completionHandler(.cancel)
return
}
completionHandler(.allow)
}
To handle the server Data, I am using following code:
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
if let toDictionary = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? NSDictionary {
print(toDictionary)
}
}
To handle the completion, I am using following code:
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if let error = error {
print(error.localizedDescription)
}
}
Additionally, I have a progress bar which I am updating as follows:
func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
let uploadProgress:Float = Float(totalBytesSent) / Float(totalBytesExpectedToSend)
let percent = Int(uploadProgress * 100)
DispatchQueue.main.async {
self.progressLabel.text = "\(percent)% completed"
self.progressView.setProgress(Float(uploadProgress), animated: true)
}
}
This code works well if the server is working fine [Response: 200].
But if I change the request URL to a dummy url like https://server/dummyfile.php, the progress bar still increments and I don't get any error since
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: #escaping (URLSession.ResponseDisposition) -> Void)
doesn't work.
Is it possible that in case of server errors [404, 500] the progress bar doesn't increment?
I am trying to communicate with Swift to a php-website using the command "uploadTask". The site is sending Data back, which is working well. The result from the website is stored in the variable "answer". But how can I actually use "answer" AFTER the uploadTask.resume() was done?
When running the file, it always prints:
"One" then "three" then "two".
I know that I could do things with "answer" right where the section "print("two")" is. And at many examples right there the command "DispatchQueue.main.async { ... }" is used. But I explicitly want to finish the uploadTask and then continue with some more calculations.
func contactPHP() {
print("One")
let url = "http://....php" // website to contact
let dataString = "password=12345" // starting POST
let urlNS = NSURL(string: url)
var request = URLRequest(url: urlNS! as URL)
request.httpMethod = "POST"
let dataD = dataString.data(using: .utf8) // convert to utf8 string
URLSession.shared.uploadTask(with: request, from: dataD)
{
(data, response, error) in
if error != nil {
print(error.debugDescription)
} else {
let answer = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)!
print("Two")
}
}.resume() // Starting the dataTask
print("Three")
// Do anything here with "answer"
}
extension NSMutableData {
func appendString(string: String) {
let data = string.data(using: String.Encoding.utf8, allowLossyConversion: true)
append(data!)
}
}
I already tried it with a completion handler. But this does not work either. This also gives me "One", "Four", "Two", "Three"
func test(request: URLRequest, dataD: Data?, completion: #escaping (NSString) -> ()) {
URLSession.shared.uploadTask(with: request, from: dataD)
{
(data, response, error) in
if error != nil {
print(error.debugDescription)
} else {
let answer = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)!
print("Two")
completion(answer)
}
}.resume() // Starting the dataTask
}
let blubb = test(request: request, dataD: dataD) { (data) in
print("Three")
}
print("Four")
Use the URLSession function that has the completion handler:
URLSession.shared.uploadTask(with: URLRequest, from: Data?, completionHandler: (Data?, URLResponse?, Error?) -> Void)
Replace your uploadTask function with something like this:
URLSession.shared.uploadTask(with: request, from: dataD) { (data, response, error) in
if let error = error {
// Error
}
// Do something after the upload task is complete
}
Apple Documentation
After you create the task, you must start it by calling its resume()
method. If the request completes successfully, the data parameter of
the completion handler block contains the resource data, and the error
parameter is nil.
If the request fails, the data parameter is nil and
the error parameter contain information about the failure. If a
response from the server is received, regardless of whether the
request completes successfully or fails, the response parameter
contains that information.
When the upload task is complete, the completion handler of the function is called. You could also implement the delegate's optional func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) function.
I am converting my code to swift 3 but getting "Type of expression is ambiguous without more context" error in the following code .
open class func performGetRequest(_ targetURL: URL!, completion: #escaping (_ data: Data?, _ HTTPStatusCode: Int, _ error: NSError?) -> Void) {
var request = URLRequest(url: targetURL)
request.httpMethod = "GET"
let sessionConfiguration = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfiguration)
let task = session.dataTask(with: request) { (data, response, error) in
DispatchQueue.main.async(execute: {
completion(data, response.statusCode,error)
})
}
task.resume()
}
The compiler wants Error not NSError.
Further in Swift 3 omit the parameter labels in the completion handler and don't pass IUO* parameters if you actually mean non-optional:
open class func performGetRequest(_ targetURL: URL, completion: #escaping (Data?, Int, Error?) -> Void) {
Finally you don't need an URLRequest in this case, GET is the default, so just pass the URL to dataTask and delete the two lines related to the request.
(*) Implicit Unwrapped Optional
There is two problem in your code first response.statusCode, response is type of URLResponse and it doesn't have property statusCode so you need to type cast it to HTTPURLResponse to get the statusCode. Second is from Swift 3 use native Error instead of NSError, So your whole code would be like this.
open class func performGetRequest(_ targetURL: URL!, completion: #escaping (_ data: Data?, _ HTTPStatusCode: Int, _ error: Error?) -> Void) {
var request = URLRequest(url: targetURL)
request.httpMethod = "GET"
let sessionConfiguration = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfiguration)
let task = session.dataTask(with: request) { (data, response, error) in
DispatchQueue.main.async(execute: {
completion(data, (response as! HTTPURLResponse).statusCode,error)
})
}
task.resume()
}
I'm using URLSession to retrieve data from an url
func scheduleURLSession() {
let backgroundConfigObject = URLSessionConfiguration.background(withIdentifier: "myIdentifier...")
let backgroundSession = URLSession(configuration: backgroundConfigObject, delegate: self, delegateQueue: nil)
let retrieveTask = backgroundSession.downloadTask(with: URL(string: "https://api.wedsblog.net/v2/index.php?bitcoin&simple")!)
retrieveTask.resume()
}
When download finished
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { }
Will be called. How can I read the content of downloaded file? Is ".downloadTask" the right function or shall I use another one?
Found following solution:
Implemented URLSessionDataDelegate
func scheduleURLSession() {
let backgroundConfigObject = URLSessionConfiguration.background(withIdentifier: "myIdentifier")
let backgroundSession = URLSession(configuration: backgroundConfigObject, delegate: self, delegateQueue: nil)
let retrieveTask = backgroundSession.dataTask(with: URL(string: "https://api.wedsblog.net/v2/index.php?bitcoin&simple")!)
retrieveTask.resume()
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
print(String(data: data, encoding: String.Encoding.utf8)!)
}
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url) {(data, response, error) -> Void in { // First check if error is not nil
// Second check what is status code of your http response
// Third do with data what you want(parse to objects)
}
I think it helpful.