2

Использование XCode-8.2.1, Swift-3.0.2 и iOS-10.2.1,Как синхронизировать последовательную очередь для задач URLSession?

Я пытаюсь вызвать две разные страницы URLSession.shared.dataTasks (первый - это простой URL-запрос, а второй - POST-запрос).

Поскольку мой первый DataTask предоставляет результат, который необходим в httpBody второй DataTask, две страницы URLSession.shared.dataTasks будут запускаться последовательно, один за другим! (а также подготовительный код должен выполняться последовательно).

Я пробовал до сих пор использовать две последовательные очереди serialQueue.sync{}. Но я должен был понять, что код не работает в том порядке, в котором я бы хотел.

Отпечаток-заявление в журнале оказывается следующим образом:

Hmmmmmm 2 
Hmmmmmm 1 
Hmmmmmm 3 

(вместо 1, 2, 3 при необходимости)!

Как вы можете получить заказ 1, 2, 3 ??

(то есть, как вы можете убедиться, что httpBody второго dataTask может быть заполнен в результате прихода с первым dataTask?)

Вот мой код: (не исполняемый файл, как URL, были вынуты - но вы понимаете)!

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. 
    } 
} 

Любая помощь приветствуется!

+0

смешно - я хочу знать то же самое 19 часов спустя! :) – Fattie

+0

вы правы, URLSession.shared.dataTask, похоже, выпрыгивает из serialQueue. – Fattie

+0

Другой способ сделать это - с рабочей очередью, которая либо (a) имеет 'maxConcurrentOperationCount'' 1'; или (b), для которых вы устанавливаете зависимости между операциями. Но для этого вам нужно сделать асинхронный подкласс 'Operation' /' NSOperation', a la http://stackoverflow.com/questions/32322386/how-to-download-multiple-files-sequently-using-nsurlsession- downloadtask-в-с/32322851 # 32322851. – Rob

ответ

2

я, наконец, нашел решение:

Вдохновленный this answer, я представил URLSessionDataDelegate, вместе с делегатом обратного вызова методов (т.е. didReceive response:, didReceive data: и didCompleteWithError error:

. Важно: Вы должны настроить ваш URLSession с делегатом, чтобы использовать методы обратного вызова URLSessionDelegate: Используйте URLSession (конфигурация: ....) для этого, как показано здесь:

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

После этого, вы хорошо идти, то есть журнал, как ожидается, в настоящее время:

Hmmmmmm 1 
Hmmmmmm 2 
Hmmmmmm 3 

Вот окончательный код (снова не исполняемый файл, как URL, были вынуты - но вы получите точку)!

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 - но вы просто вызываете их последовательно. правильно? вы не используете serialQueue, я не думаю? – Fattie

+0

iKK - Это прекрасно. Но, очевидно, вам не нужно проходить всю эту работу с помощью делегированной реализации.Вы можете выполнить то же самое с гораздо более простым шаблоном обработчика завершения. – Rob

+0

Спасибо, Роб. У вас есть такой пример, чтобы показать здесь? – iKK

Смежные вопросы