Swift - multiple Chain http request with loop

霸气de小男生 提交于 2019-12-06 09:32:01

The basic idea would be to write a routine that retries the request in question, and only fulfills the promise if certain criteria are met:

/// Attempt a network request.
///
/// - Parameters:
///   - request:    The request.
///   - maxRetries: The maximum number of attempts to retry (defaults to 100).
///   - attempt:    The current attempt number. You do not need to supply this when you call this, as this defaults to zero.
///   - fulfill:    The `fulfill` closure of the `Promise`.
///   - reject:     The `reject` closure of the `Promise.

private func retry(_ request: URLRequest, maxRetries: Int = 100, attempt: Int = 0, fulfill: @escaping (Data) -> Void, reject: @escaping (Error) -> Void) {
    guard attempt < maxRetries else {
        reject(RetryError.tooManyRetries)
        return
    }

    Alamofire.request(request)
        .validate()
        .responseData { response in
            switch response.result {
            case .success(let value):
                let taskCompleted = ...          // determine however appropriate for your app
                let serverReportedFailure = ...

                if serverReportedFailure {
                    reject(RetryError.taskFailed)
                } else if taskCompleted {
                    fulfill(value)
                } else {
                    DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
                        self.retry(request, maxRetries: maxRetries, attempt: attempt + 1, fulfill: fulfill, reject: reject)
                    }
                }
            case .failure(let error):
                reject(error)
            }
    }
}

/// Error codes for retrying of network requests.

enum RetryError: Error {
    case tooManyRetries
    case taskFailed
}

You can then have a method that creates the promise that is satisfied by the above:

/// Create a promise for a network request that will be retried until
/// some criteria is met.
///
/// - Parameter request: The request to be attempted.
/// - Returns: The `Promise`.

private func retry(for request: URLRequest) -> Promise<Data> {
    return Promise { fulfill, reject in
        self.retry(request, fulfill: fulfill, reject: reject)
    }
}

You can now do the standard Promise stuff with the above, e.g.:

retry(for: request).then { data in
    print("received \(data)")
}.catch { error in
    print("error: \(error)")
}

A few caveats in the above:

  • I'm calling fulfill with a Data. Usually you'd have some model object or the like, but I'm not sure what was appropriate in your case.

  • I'm obviously not doing any XML parsing. But you'd obviously parse the response and determine taskCompleted accordingly.

  • Although you said the status of the task could either be "queued", "in progress", and "completed", I assumed you'll eventually have your server's process adding some error handling if the queued task failed. So, I also added a taskFailed error. Do as you see fit.

  • I made some assumptions about maximum number of retries and what to do if there was a network error. Clearly, customize these conditions to whatever is appropriate in your case.

So, don't get lost in the details of my example, but rather focus on the broader picture, namely that you can have a routine that creates a Promise and passes the two fulfill and reject closures to a separate routine that actually performs the retry logic.

See chaining https-requests without Alamofire: POST+GET+GET+GET...

class ViewController1: UIViewController, URLSessionDataDelegate {
    var URLSessionConfig :URLSessionConfiguration!
    var session: URLSession?
    var task0: URLSessionTask!
    var task1: URLSessionTask!
    var task2: URLSessionTask!
    var task3: URLSessionTask!

    override func viewDidLoad() {
        super.viewDidLoad()
        ...
        self.URLSessionConfig = URLSessionConfiguration.ephemeral
        #if available
        self.URLSessionConfig.waitsForConnectivity = true
        #endif
        self.session = URLSession(configuration: URLSessionConfig, delegate: self, delegateQueue: OperationQueue.main)
    }

    func Start() {
        let url0 = URL(string: "https://myserver/PRIMARY_PATH/")!
        var req0 = URLRequest(url: url0)
        req0.setValue("application/octet-stream", forHTTPHeaderField: "Content-Type")
        req0.httpMethod = "POST"
        req0.httpBody = str.data(using: .utf8)
        self.task0 = self.session?.dataTask(with: req0 as URLRequest)
        self.task0.resume()
    }

    func parseData0(didReceive data: Data) -> URLRequest? {
        do {
            let json = try JSONSerialization.jsonObject(with: data) as? [String: String]
            ...
            let url1 = URL(string: "https://myserver/SECONDARY_PATH/"+...)!
            var req1 = URLRequest(url: url1)
            req1.httpMethod = "GET"            
            return req1
        }
        catch let parseError {
            debugPrint("parsing error: \(parseError)")
            return nil
        }
    }
    func parseData1(didReceive data: Data) -> URLRequest? {
        do {
            let json = try JSONSerialization.jsonObject(with: data) as? [String: Any]
            ...
        }
        catch let parseError {
            debugPrint("parsing error: \(parseError)")
            return nil
        }
    }

    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
        debugPrint("Data received: \(data)")
        if dataTask == self.task0 {
            let req1: URLRequest? = parseData0(didReceive: data)
            if req1 != nil {
                DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
                    self.task1 = self.session?.dataTask(with: req1!)
                    self.task1.resume()
                }
            }
        }
        if dataTask == self.task1 {
            let req1: URLRequest? = parseData1(didReceive: data)
            if req1 != nil {
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
                    self.task2 = self.session?.dataTask(with: req1!)
                    self.task2.resume()
                }
            }
        }
        if dataTask == self.task2 {
            let req1: URLRequest? = parseData1(didReceive: data)
            if req1 != nil {
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
                    self.task3 = self.session?.dataTask(with: req1!)
                    self.task3.resume()
                }
            }
        }
        if dataTask == self.task3 {
            let req1: URLRequest? = parseData1(didReceive: data)
            if req1 != nil {
                ...
            }
        }
    }

    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {        
        debugPrint("Response received: \(response)")
        completionHandler(URLSession.ResponseDisposition.allow)
    }

    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        if error != nil {
            debugPrint("error message: \(error!)")
            debugPrint("code: \(error!._code)")
        }
    }

    func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
        debugPrint("there was an error: \(error?.localizedDescription ?? "")")
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!