I would like to process data as it comes in, so I've instiated a URL session like so:
let session = URLSession(configuration: sessionConfiguration, delegate: self, delegateQueue: operationQueue);
I also set the class to be a URLSessionDataDelegate:
class ViewController: UITableViewController, URLSessionDataDelegate{
Lastly, I implement the didReceive data function like this:
func urlSession(_ session: URLSession,
dataTask: URLSessionDataTask,
didReceive data: Data){
print(data)
}
However, the function is never being called.
I run my session like this:
let session1 = session.dataTask(with: url) { (data, response, error) in
print(data!);
}
It prints the data from the callback, but not from the delegate. Any help is greatly appreciated.
EDIT:
I also added the following:
func urlSession(_ session: URLSession,
dataTask: URLSessionDataTask,
didReceive response: URLResponse,
completionHandler: #escaping (URLSession.ResponseDisposition) -> Void){
completionHandler(URLSession.ResponseDisposition.allow);
}
However, the delegate method is still not being called.
The reason this is not working is that you're creating your data task with a completion handler. Instead you must use the dataTask(with:) function instead.
I also missed this in the documentation and spent hours wondering why my delegate methods weren't being called.
From the docs:
By using the completion handler, the task bypasses calls to delegate methods for response and data delivery, and instead provides any resulting
NSData, URLResponse, and NSError objects inside the completion handler. Delegate methods for handling authentication challenges, however, are still called.
Related
I'm trying to realise basic-authentication using URLSessionDelegate and URLCredentials
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Swift.Void)
Apple recommends not to write manually Authorization header. So I'm trying to use URLCredentials.
I'm doing very simple example in Swift Playground:
class Callback: NSObject {}
extension Callback: URLSessionDelegate {
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Swift.Void) {
print("received challenge")
print(challenge.protectionSpace.authenticationMethod)
completionHandler(.performDefaultHandling, nil)
}
}
let callback = Callback()
var configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: callback, delegateQueue: nil)
let string = "http://demo0230490.mockable.io/test"
var request = URLRequest(url: URL(string: string)!)
session.dataTask(with: request) { (data, response, error) in
print("response")
print(response)
}.resume()
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
The problem is – I don't receive auth challenge.
If you go to http://demo0230490.mockable.io/test in browser you will see the browser showing alert for basic auth.
From my mock http I return header like:
"Www-Authenticate" = (
"Basic realm=\"Access to MRM\""
);
Also I tried switching to https (I mean https://demo0230490.mockable.io/test) to check whether I'll receive auth challenge of type NSURLAuthenticationMethodServerTrust.
And that works. That means that my delegate is properly set and works in some circumstances.
So the question is:
Can anybody point the problem or provide the working example for basic auth using URLCredentials (for example using https://www.mockable.io)?
According to the documentation urlSession(_:didReceive:completionHandler:) is called only in two situations:
When a remote server asks for client certificates or Windows NT LAN Manager (NTLM) authentication, to allow your app to provide
appropriate credentials
When a session first establishes a connection to a remote server that uses SSL or TLS, to allow your app to verify the server’s
certificate chain
When you are switching to https the second condition is met and therefore method is called.
There is two types of authentication challenges: session-level and non-session-level.
For non-session-level challenges URLSession calls urlSession(_:task:didReceive:completionHandler:). For session-level challenges URLSession calls urlSession(_:didReceive:completionHandler:).
Difference between session-level challenges and non-session-level challenges is described in the discussion section of urlSession(_:task:didReceive:completionHandler:) documentation:
For session-level challenges—NSURLAuthenticationMethodNTLM, NSURLAuthenticationMethodNegotiate,
NSURLAuthenticationMethodClientCertificate, or
NSURLAuthenticationMethodServerTrust—the NSURLSession object calls the
session delegate’s urlSession(:didReceive:completionHandler:) method.
If your app does not provide a session delegate method, the
NSURLSession object calls the task delegate’s
urlSession(:task:didReceive:completionHandler:) method to handle the
challenge.
For non-session-level challenges (all others), the URLSession object calls the session delegate’s
urlSession(:task:didReceive:completionHandler:) method to handle the
challenge. If your app provides a session delegate and you need to
handle authentication, then you must either handle the authentication
at the task level or provide a task-level handler that calls the
per-session handler explicitly. The session delegate’s
urlSession(:didReceive:completionHandler:) method is not called for
non-session-level challenges.
In your case you should implement non-session-level challenge handler:
extension Callback: URLSessionTaskDelegate {
func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
print("received task challenge")
}
}
Also there is working example on Apple-forum.
You should use urlSession(_:task:didReceive:completionHandler:) of URLSessionTaskDelegate. Method you use is for connection-level challenge, not for app-level.
Using either NSURLConnection or NSURLSession, how can I tell when data has started to arrive? My download may be large or my connection slow so I want to make an update to the UI once data starts to arrive.
NSUrlSession has a delegate that should help you, if you're interested in knowing the each block of data is received.
func urlSession(_ session: URLSession,
dataTask: URLSessionDataTask,
didReceive data: Data)
And this delegate if just the headers received is useful.
optional func urlSession(_ session: URLSession,
dataTask: URLSessionDataTask,
didReceive response: URLResponse,
completionHandler: #escaping (URLSession.ResponseDisposition) -> Void)
I am trying to upload a file to the server and after the completion of upload, I'm supposed to get a JSON response.
I can see the data when I use the completion block
uploaderSession.dataTask(with: request, completionHandler: { (data, response, error) in
print("\(NSString(data: data!, encoding: 4))")
}).resume()
I need the same data but using the custom delegates
let session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
session.dataTask(with: request).resume()
I implemented
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data)
but didReceiveData is not called.
Please suggest what can be done to get the JSON data at the end of completion of file upload
Thanks in advance
ok so got the answer just posting cause it can help somebody
we have to call
completionHandler(URLSession.ResponseDisposition.allow)
in
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: #escaping (URLSession.ResponseDisposition) -> Void)
I want to download a file from a certain url .
what I need is the data delegate ( the delegate which gives me the downloaded data) .
I implemented the :
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
delegate but it doesn't get called .
my code is :
func download(url: URL)
{
let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig, delegate: self, delegateQueue: nil)
let task = session.downloadTask(with: url)
task.resume()
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
print("apending ")
self.dataaa.append(data)
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: #escaping (URLSession.ResponseDisposition) -> Void) {
print("here")
}
the two delegate functions don't get called !
You're using the delegate methods for a data task, but you're creating a download task. Those two task types work differently and use completely different delegate methods.
I am having trouble with a URLSession that creates and resumes a URLSessionDataTask in the sense that I expect subsequent calls to certain methods in the session's (and task`s) delegate but apparently no such calls occur.
How can I debug URLSession in a situation like this? For instance, can I request log output from it e.g. to find out when it sends or receives HTTP traffic or when it attempts to call a method in the delegate but cannot find the relevant implementation (e.g. because the method signature in the implementation may be slightly off).
Delegate methods are now (starting to be) called as expected. The solution consisted in copying method signatures from the very code base of URLSessionDataDelegate and URLSessionTaskDelegate (not their documentation) to ensure exact matches. Looking at device log output (lines with marked libsystem_network.dylib and <Debug>) was also helpful. The employed methods signatures are now as follows:
public func urlSession(_ session: URLSession,
task: URLSessionTask,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Swift.Void) {
// ...
}
public func urlSession(_ session: URLSession,
dataTask: URLSessionDataTask,
didReceive data: Data) {
// ...
}
public func urlSession(_ session: URLSession,
task: URLSessionTask,
didCompleteWithError error: Error?) {
// ...
}