2

Utilisation de XCode-8.2.1, Swift-3.0.2 et iOS-10.2.1,Comment synchroniser la file d'attente série pour les tâches URLSession?

J'essaie d'appeler deux URLSession.shared.dataTasks différentes (la première est une simple demande d'URL et la seconde est une requête POST).

Depuis que mon premier DataTask fournit un résultat qui est nécessaire dans le httpBody de la seconde DataTask, les deux URLSession.shared.dataTasks doivent fonctionner en série, l'une après l'autre! (et aussi le code préparatif doit courir consécutivement).

J'ai essayé, jusqu'à présent, d'utiliser deux files d'attente serialQueue.sync{} consécutives. Mais je devais réaliser que le code ne fonctionne pas dans l'ordre que je voudrais.

La-déclaration d'impression dans le tour journal révèle être comme suit:

Hmmmmmm 2 
Hmmmmmm 1 
Hmmmmmm 3 

(au lieu de 1, 2, 3 au besoin)!

Comment pouvez-vous obtenir l'ordre 1, 2, 3?

(à savoir, comment pouvez-vous vous assurer que le httpBody du second dataTask peut être rempli d'un résultat provenant de la première dataTask?)

Voici mon code: (non exécutable comme URL ont été prises en - mais vous avez compris)!

import UIKit 

class ViewController: UIViewController { 

    let serialQueue = DispatchQueue(label: "myResourceQueue") 
    var stationID: Int = 0 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     // Do any additional setup after loading the view, typically from a nib. 


     self.serialQueue.sync { 
      let myResourceURL = URL(string: "myQueryString1") 
      let task = URLSession.shared.dataTask(with: myResourceURL!) { (data, response, error) in 
       if (error != nil) { 
        // print(error.debugDescription) 
       } else { 
        if let myData = data { 
         do { 
          let myJson = try JSONSerialization.jsonObject(with: myData, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject 
          // print(myJson) 
          // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
          print("Hmmmmmm 1") 
          // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
         } catch { 
          // error 
         } 
        } 
       } 
      } 
      task.resume() 
     } 
     self.serialQueue.sync { 
      var request = URLRequest(url: URL(string: "myQueryString2")!) 
      request.httpMethod = "POST" 
      request.addValue("API_id", forHTTPHeaderField: "Authorization") 
      request.addValue("application/xml", forHTTPHeaderField: "Content-Type") 
      // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
      print("Hmmmmmm 2") 
      // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
      let postString: String = "My_XML_POST_Body" 
      request.httpBody = postString.data(using: .utf8) 
      let task = URLSession.shared.dataTask(with: request) { data, response, error in 
       // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
       print("Hmmmmmm 3") 
       // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
      } 
      task.resume() 
     } 
    } 

    override func didReceiveMemoryWarning() { 
     super.didReceiveMemoryWarning() 
     // Dispose of any resources that can be recreated. 
    } 
} 

Toute aide appréciée!

+0

drôle - Je veux savoir la même chose 19 heures plus tard! :) – Fattie

+0

vous avez raison, URLSession.shared.dataTask semble sortir de serialQueue. – Fattie

+0

Une autre façon de procéder est avec une file d'attente d'opération qui (a) a 'maxConcurrentOperationCount' de' 1'; ou (b) pour lequel vous établissez des dépendances entre les opérations. Mais pour que cela fonctionne, vous devez faire une sous-classe asynchrone 'Operation' /' NSOperation', à la http://stackoverflow.com/questions/32322386/how-to-download-multiple-files-sequentially-using-nsurlsession- downloadtask-in-s/32322851 # 32322851. – Rob

Répondre

2

J'ai finalement trouvé une solution:

Inspiré par this answer, j'ai présenté un URLSessionDataDelegate, ainsi que ses RAPPELLONS méthodes de délégué (c.-à-didReceive response:, didReceive data: et didCompleteWithError error:

Important:. Vous devez configurer votre URLSession avec un délégué pour que les méthodes de rappel de l'URLSessionDelegate introduites fonctionnent: Utilisez URLSession (configuration: ....) pour ceci comme montré ici:

let URLSessionConfig = URLSessionConfiguration.default 
let session = URLSession(configuration: URLSessionConfig, delegate: self, delegateQueue: OperationQueue.main) 

Après cela, vous êtes bon pour aller, à savoir le journal est maintenant comme prévu:

Hmmmmmm 1 
Hmmmmmm 2 
Hmmmmmm 3 

Voici le code final (encore une fois non exécutable comme URL ont été prises - mais vous obtenez le point)!

import UIKit 

class ViewController: UIViewController, URLSessionDataDelegate { 

    var stationID: Int = 0 
    let URLSessionConfig = URLSessionConfiguration.default 
    var session: URLSession? 
    var task1: URLSessionTask? 
    var task2: URLSessionTask? 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     // Do any additional setup after loading the view, typically from a nib. 

     self.session = URLSession(configuration: URLSessionConfig, delegate: self, delegateQueue: OperationQueue.main) 

     // prepare dataTask Nr 1 
     let myResourceURL = URL(string: "myQueryString1") 
     self.task1 = session?.dataTask(with: myResourceURL!) 

     // start dataTask Nr 1 (URL-request) 
     self.task1?.resume() 
    } 

    // Optional: Use this method if you want to get a response-size information 
    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) { 

     // print(Int(response.expectedContentLength)) 
     completionHandler(URLSession.ResponseDisposition.allow) 
    } 

    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { 

     if dataTask == self.task1 { 
      do { 
       let myJson = try JSONSerialization.jsonObject(with: myData, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject 
       // print(myJson) 
       // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
       print("Hmmmmmm 1") 
       // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 

       // prepare dataTask Nr 2 
       self.task2 = self.session?.dataTask(with: self.prepareMyURLRequest()) 
      } catch { 
       // error 
      } 
     } else if dataTask == self.task2 { 

      // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
      print("Hmmmmmm 3") 
      // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 

     } else { 
      print("unknown dataTask callback") 
     } 
    } 

    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { 
     if (error != nil) { 
      // print(error.debugDescription) 
     } else if task == self.task1 { 

      // start dataTask Nr 2 (POST URL-request) 
      self.task2?.resume() 
     } 
    } 

    func prepareMyURLRequest() -> URLRequest { 
     var request = URLRequest(url: URL(string: "myQueryString2")!) 
     request.httpMethod = "POST" 
     request.addValue("API_id", forHTTPHeaderField: "Authorization") 
     request.addValue("application/xml", forHTTPHeaderField: "Content-Type") 
     // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
     print("Hmmmmmm 2") 
     // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
     let postString: String = "My_XML_POST_Body" 
     request.httpBody = postString.data(using: .utf8) 
     return request 
    } 

    override func didReceiveMemoryWarning() { 
     super.didReceiveMemoryWarning() 
     // Dispose of any resources that can be recreated. 
    } 

} 
+0

iKK - mais, vous les appelez simplement séquentiellement. droite? vous n'utilisez pas serialQueue, je ne pense pas ?? – Fattie

+0

iKK - C'est bien. Mais, évidemment, vous n'avez pas à faire tout ce travail avec une implémentation basée sur les délégués.Vous pouvez accomplir exactement la même chose avec un modèle de gestionnaire de complétion beaucoup plus simple. – Rob

+0

Merci Rob. Avez-vous un tel exemple à montrer ici? – iKK