How to sync serial queue for URLSession tasks?

泄露秘密 提交于 2019-12-04 07:04:50
iKK

I finally found a solution:

Inspired by this answer, I introduced a URLSessionDataDelegate, together with its delegate callback-methods (i.e. didReceive response:, didReceive data: and didCompleteWithError error:.

Important: You need to set up your URLSession with a delegate in order to make the introduced URLSessionDelegate's callback methods work: Use URLSession(configuration: ....) for this like shown here:

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

After that, you are good to go, i.e. the log is as expected now:

Hmmmmmm 1
Hmmmmmm 2
Hmmmmmm 3

Here is the final code (again not executable as URL's were taken out - but you get the 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.
    }

}

If task2 needs the results from task1 you should start task2 from the completion block of task1

URLSession.shared.dataTask(with: request) { data, response, error in
    // process task1 and setup request2
    URLSession.shared.dataTask(with: request2) { data, response, error in
        // process task2
    }.resume()
}.resume()

Of course, this will get a bit unwieldy with multiple requests, so it might be better to use Promises & Futures. There are several implementations of Promises & Futures for Swift, e.g. Promis.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!