Swift - 循环

时间:2016-11-19 08:23:33

标签: ios swift alamofire promisekit

从2天开始,我感觉我正在搜索整个网络,以解决我的多个http请求问题。所以我的工作流程如下:

  1. 将图像上传到服务器

    • 响应=具有任务ID的XML格式
  2. 使用任务ID向服务器发出GET请求,以检查此任务的状态。

    • 响应= XML格式,状态可以是"已完成","正在进行","已排队"
    • 如果状态!="已完成" - 重试第2步
    • 如果状态=="已完成" - 转到第3步
  3. 从resultUrl

  4. 下载结果

    我的最后一次尝试是使用PromiseKit以一种干净的方式链接请求,如本文所述:Chain multiple Alamofire requests。但如果状态尚未完成,我不知道如何每2-5秒循环第二步。

    此工作流程是否有推荐的解决方案?这是我对PromiseKit的测试,我在没有循环的情况下成功链接了请求:

    let request = Client.imageUploadRequest(image: imageView.image!)
    let httpOperation = HTTPOperation(withRequest: request)
    
    httpOperation.sendRequest().then() { result -> Promise<String> in
        let xml = SWXMLHash.parse(result)
        let id = self.getXMLAttribute(from: xml, with: "id")!
        let taskStatusrequest =  Client.getTaskStatusRequest(withTaskID: id)
        let httpOperation = HTTPOperation(withRequest: taskStatusrequest)
    
        return httpOperation.sendRequest()
    }
    // Loop this result if status != "Completed"
    .then { result -> Promise<Data> in
        let xml = SWXMLHash.parse(result)
        let downloadUrl = self.getXMLAttribute(from: xml, with: "resultUrl")!
        let downloadRequest = Client.getDownloadRequest(withUrl: downloadUrl)
        let httpOperation = HTTPOperation(withRequest: downloadRequest)
    
        // if status != "Completed" don't return, retry this step
        return httpOperation.downloadData()
    }
    .then { _ -> Void in
        // Refresh View with data
    }
    

2 个答案:

答案 0 :(得分:2)

基本思路是编写一个重试相关请求的例程,只有满足某些条件才能履行承诺:

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

然后,您可以创建一个方法来创建上述满足的承诺:

/// 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)
    }
}

您现在可以使用上述内容执行标准Promise内容,例如:

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

上面的一些警告:

  • 我正在使用fulfill致电Data。通常你会有一些模型对象等,但我不确定你的情况是什么。

  • 我显然没有进行任何XML解析。但是您显然会解析响应并相应地确定taskCompleted

  • 虽然您说任务的状态可能是“排队”,“正在进行”和“已完成”,但我认为如果排队任务失败,我最终会让您的服务器进程添加一些错误处理。所以,我还添加了taskFailed错误。按你的意愿做。

  • 我做了一些关于最大重试次数的假设以及如果出现网络错误该怎么办。显然,请根据您的情况自定义这些条件。

所以,不要迷失在我的例子的细节中,而是关注更广泛的图景,即你可以有一个例程创建一个Promise并传递两个fulfillreject关闭实际执行重试逻辑的单独例程。

答案 1 :(得分:0)

请参阅在不使用Alamofire的情况下链接https-请求: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 ?? "")")
    }
}