Je fais une URLSession, mais l'URL requiert des informations d'identification.
J'ai toute cette méthode ici qui essaie de faire une URLSession avec URLCredentials:
func loginUser(_ username: String, password: String, completion: @escaping (_ result: Bool) -> Void)
{
//Create request URL as String
let requestString = String(format:"%@", webservice) as String
//Covert URL request string to URL
guard let url = URL(string: requestString) else {
print("Error: cannot create URL")
return
}
//Convert URL to URLRequest
let urlRequest = URLRequest(url: url)
print(urlRequest)
//Add the username and password to URLCredential
credential = URLCredential(user:username, password:password, persistence: .forSession)
//Setup the URLSessionConfiguration
let config = URLSessionConfiguration.default
//Setup the URLSession
let session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
//Prepare the task to get data.
let task = session.dataTask(with: urlRequest, completionHandler: { (data, response, error) in
DispatchQueue.main.async(execute: {
if(error == nil)
{
completion(true)
}
else
{
completion(false)
}
})
})
//Run the task to get data.
task.resume()
}
et voici mes méthodes URLSessionDelegate:
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
if challenge.previousFailureCount > 0
{
completionHandler(Foundation.URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge, nil)
}
else
{
completionHandler(Foundation.URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust:challenge.protectionSpace.serverTrust!))
}
}
/**
Requests credentials from the delegate in response to an authentication request from the remote server.
*/
func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
completionHandler(Foundation.URLSession.AuthChallengeDisposition.useCredential,credential)
}
Je remarque quand je débogue ceci dans cette méthode déléguée:
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
if challenge.previousFailureCount > 0
{
completionHandler(Foundation.URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge, nil)
}
else
{
completionHandler(Foundation.URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust:challenge.protectionSpace.serverTrust!))
}
}
Que cette méthode soit appelée deux fois et lorsqu'elle frappe cette ligne pour la deuxième fois:
completionHandler(Foundation.URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust:challenge.protectionSpace.serverTrust!))
Je reçois cette erreur:
fatal error: unexpectedly found nil while unwrapping an Optional value
et puis mon application se bloque! Comment puis-je corriger cette erreur?
le plantage est dû au fait que challenge.protectionSpace.serverTrust
soit à zéro lorsque vous essayez de forcer le dérouler.
vous devriez décompresser serverTrust
et le gérer comme étant nul. Je suppose que lorsque serverTrust
est nul, challenge.error
a une valeur.
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
if challenge.previousFailureCount > 0 {
completionHandler(Foundation.URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge, nil)
} else if let serverTrust = challenge.protectionSpace.serverTrust {
completionHandler(Foundation.URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust: serverTrust))
} else {
print("unknown state. error: \(challenge.error)")
// do something w/ completionHandler here
}
}
Voici la syntaxe selon Swift 3.
Il suffit de vérifier d'abord dans quelle partie de la méthode de délégation il a entré
open func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Swift.Void){
var disposition: URLSession.AuthChallengeDisposition = URLSession.AuthChallengeDisposition.performDefaultHandling
var credential:URLCredential?
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
if (credential != nil) {
disposition = URLSession.AuthChallengeDisposition.useCredential
}
else{
disposition = URLSession.AuthChallengeDisposition.performDefaultHandling
}
}
else{
disposition = URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge
}
if (completionHandler != nil) {
completionHandler(disposition, credential);
}
}
Voici une réflexion légèrement refactorisée à partir des réponses ci-dessus, une classe déléguée vérifie le nombre d'échecs, utilise la valeur par défaut sauf si le défi est de type confiance serveur, elle appelle alors l'achèvement avec les informations d'identification de confiance:
class AuthSessionDelegate: NSObject, URLSessionDelegate {
func urlSession(_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
let authMethod = challenge.protectionSpace.authenticationMethod
guard challenge.previousFailureCount < 1, authMethod == NSURLAuthenticationMethodServerTrust,
let trust = challenge.protectionSpace.serverTrust else {
completionHandler(.performDefaultHandling, nil)
return
}
completionHandler(.useCredential, URLCredential(trust: trust))
}
}