web-dev-qa-db-fra.com

Swift 3 - Envoyer une requête http synchrone

J'ai le code suivant:

func completeLoadAction(urlString:String) -> Int {
    let url = URL(string:urlString.trimmingCharacters(in: .whitespaces))
    let request = URLRequest(url: url!)
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        guard let data = data, error == nil else {                                                 // check for fundamental networking error
            print("error=\(error)")
            let ac = UIAlertController(title: "Unable to complete", message: "The load has been added to the completion queue. This will be processed once there is a connection.", preferredStyle: .alert)
            ac.addAction(UIAlertAction(title: "OK", style: .default))
            self.present(ac, animated:  true)
            return
        }

    let httpStatus = response as? HTTPURLResponse
        var httpStatusCode:Int = (httpStatus?.statusCode)!

        let responseString = String(data: data, encoding: .utf8)
        print("responseString = \(responseString)")
        let ac = UIAlertController(title: "Completed Successfully", message: "The "+coldel+" has been completed successfully", preferredStyle: .alert)
        ac.addAction(UIAlertAction(title:"Continue", style: .default, handler:  { action in self.performSegue(withIdentifier: "segueConfirmedLoad", sender: self) }))

        self.present(ac, animated: true)

    }
    task.resume()
    return httpStatusCode
}

Je dois pouvoir l'appeler et en même temps vérifier la valeur de retour car c'est le code d'état http, il me fera savoir si l'appel a réussi ou non.

Le problème est parce que c'est dans une tâche de données, je ne peux pas accéder au code d'état des réponses ici

var httpStatusCode:Int = (httpStatus?.statusCode)!

Parce que la tâche ne démarre pas tant que Task.Resume () n'est pas appelée et la tâche est asynchrone donc elle ne fonctionnera jamais.

Y a-t-il des moyens de contourner cela?

11
Alec.

Il existe toujours un moyen d'utiliser le modèle asynchrone.

Pour rendre la fonction asynchrone, ajoutez un bloc d'achèvement

func completeLoadAction(urlString:String, completion: (Int) -> ()) {
   let url = URL(string:urlString.trimmingCharacters(in: .whitespaces))
   let request = URLRequest(url: url!)
   let task = URLSession.shared.dataTask(with: request) { data, response, error in
      guard let data = data, error == nil else {                                                 // check for fundamental networking error
         print("error=\(error)")
         DispatchQueue.main.async {
            let ac = UIAlertController(title: "Unable to complete", message: "The load has been added to the completion queue. This will be processed once there is a connection.", preferredStyle: .alert)
            ac.addAction(UIAlertAction(title: "OK", style: .default))
            self.present(ac, animated:  true)
         }
         completion(0) // or return an error code 
         return     
      }

      let httpStatus = response as? HTTPURLResponse
      var httpStatusCode:Int = (httpStatus?.statusCode)!

      let responseString = String(data: data, encoding: .utf8)
      print("responseString = \(responseString)")
      DispatchQueue.main.async {
         let ac = UIAlertController(title: "Completed Successfully", message: "The "+coldel+" has been completed successfully", preferredStyle: .alert)
         ac.addAction(UIAlertAction(title:"Continue", style: .default, handler:  { action in self.performSegue(withIdentifier: "segueConfirmedLoad", sender: self) }))
         self.present(ac, animated: true)
      }
      completion(httpStatusCode)
   }
   task.resume()

}

et l'appelle ainsi

completeLoadAction(urlString: "www.something.com") { code in
   print(code)
}
12
vadian

Pour le rendre synchrone et attendre, vous pouvez utiliser des sémaphores comme ci-dessous

struct Login {

    static func execute() -> Bool {
        let request = NSURLRequest....

        var success = false
        let semaphore = DispatchSemaphore(value: 0)
        let task = URLSession.shared.dataTask(with: request, completionHandler: { _, response, error in
            if let error = error {
                print("Error while trying to re-authenticate the user: \(error)")
            } else if let response = response as? HTTPURLResponse,
                300..<600 ~= response.statusCode {
                    print("Error while trying to re-authenticate the user, statusCode: \(response.statusCode)")
            } else {
                success = true
            }
            semaphore.signal()
        }) 

        task.resume()
        _ = semaphore.wait(timeout: DispatchTime.distantFuture)
        return success
    }
}
34
darren102

Cela ne fonctionnera pas dans toutes les situations. Supposons que vous implémentez une extension partagée. Et vous remplacez la méthode isContentValid() qui retourne un booléen (vrai si le contenu est valide) ... mais afin de tester si le contenu est valide, vous voulez vérifier que le serveur est en cours d'exécution (ce est un exemple artificiel). Si vous effectuez un appel http asynchrone - bloc d'achèvement ou non - vous ne pouvez pas renvoyer la valeur appropriée du booléen; la seule façon d'y parvenir est de faire un appel synchrone et de renvoyer vrai/faux en fonction de la valeur de retour.

La réponse qui a publié le modèle de sémaphore est la bonne à utiliser dans ce cas.

0
user9107690