Swift - multiple Chain http request with loop












2















Since 2 days it feels that I'm searching the whole web to solve my problem with multiple http requests. So my workflow looks like this:





  1. Upload a image to a server




    • Response = XML Format with a Task ID




  2. GET request to the server with the Task ID to check the status of this task.




    • Response = XML Format where the status could be "Completed", "In Progress", "Queued"

    • If Status != "Completed" - retry step 2

    • If Status == "Completed" - go to step 3



  3. Download the result from the resultUrl



My last try was to use PromiseKit to chain the requests in a clean way like described in this post: Chain multiple Alamofire requests. But I don't know how to loop the second step every 2-5 seconds if the status isn't completed yet.



Is there a recommended solution for this workflow? This was my test with PromiseKit, where i successfully chained the requests without a loop:



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
}









share|improve this question





























    2















    Since 2 days it feels that I'm searching the whole web to solve my problem with multiple http requests. So my workflow looks like this:





    1. Upload a image to a server




      • Response = XML Format with a Task ID




    2. GET request to the server with the Task ID to check the status of this task.




      • Response = XML Format where the status could be "Completed", "In Progress", "Queued"

      • If Status != "Completed" - retry step 2

      • If Status == "Completed" - go to step 3



    3. Download the result from the resultUrl



    My last try was to use PromiseKit to chain the requests in a clean way like described in this post: Chain multiple Alamofire requests. But I don't know how to loop the second step every 2-5 seconds if the status isn't completed yet.



    Is there a recommended solution for this workflow? This was my test with PromiseKit, where i successfully chained the requests without a loop:



    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
    }









    share|improve this question



























      2












      2








      2


      1






      Since 2 days it feels that I'm searching the whole web to solve my problem with multiple http requests. So my workflow looks like this:





      1. Upload a image to a server




        • Response = XML Format with a Task ID




      2. GET request to the server with the Task ID to check the status of this task.




        • Response = XML Format where the status could be "Completed", "In Progress", "Queued"

        • If Status != "Completed" - retry step 2

        • If Status == "Completed" - go to step 3



      3. Download the result from the resultUrl



      My last try was to use PromiseKit to chain the requests in a clean way like described in this post: Chain multiple Alamofire requests. But I don't know how to loop the second step every 2-5 seconds if the status isn't completed yet.



      Is there a recommended solution for this workflow? This was my test with PromiseKit, where i successfully chained the requests without a loop:



      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
      }









      share|improve this question
















      Since 2 days it feels that I'm searching the whole web to solve my problem with multiple http requests. So my workflow looks like this:





      1. Upload a image to a server




        • Response = XML Format with a Task ID




      2. GET request to the server with the Task ID to check the status of this task.




        • Response = XML Format where the status could be "Completed", "In Progress", "Queued"

        • If Status != "Completed" - retry step 2

        • If Status == "Completed" - go to step 3



      3. Download the result from the resultUrl



      My last try was to use PromiseKit to chain the requests in a clean way like described in this post: Chain multiple Alamofire requests. But I don't know how to loop the second step every 2-5 seconds if the status isn't completed yet.



      Is there a recommended solution for this workflow? This was my test with PromiseKit, where i successfully chained the requests without a loop:



      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
      }






      ios swift alamofire promisekit






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited May 23 '17 at 12:30









      Community

      11




      11










      asked Nov 19 '16 at 8:23









      kaaPekaaPe

      133




      133
























          2 Answers
          2






          active

          oldest

          votes


















          2














          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.






          share|improve this answer


























          • Thank you. This is a perfect example for my case. Now i can clean up my workaround to get back a clean code!

            – kaaPe
            Nov 21 '16 at 11:10



















          0














          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 ?? "")")
          }
          }





          share|improve this answer























            Your Answer






            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "1"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f40690726%2fswift-multiple-chain-http-request-with-loop%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            2 Answers
            2






            active

            oldest

            votes








            2 Answers
            2






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            2














            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.






            share|improve this answer


























            • Thank you. This is a perfect example for my case. Now i can clean up my workaround to get back a clean code!

              – kaaPe
              Nov 21 '16 at 11:10
















            2














            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.






            share|improve this answer


























            • Thank you. This is a perfect example for my case. Now i can clean up my workaround to get back a clean code!

              – kaaPe
              Nov 21 '16 at 11:10














            2












            2








            2







            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.






            share|improve this answer















            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.







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Nov 20 '16 at 8:09

























            answered Nov 20 '16 at 7:53









            RobRob

            303k49566735




            303k49566735













            • Thank you. This is a perfect example for my case. Now i can clean up my workaround to get back a clean code!

              – kaaPe
              Nov 21 '16 at 11:10



















            • Thank you. This is a perfect example for my case. Now i can clean up my workaround to get back a clean code!

              – kaaPe
              Nov 21 '16 at 11:10

















            Thank you. This is a perfect example for my case. Now i can clean up my workaround to get back a clean code!

            – kaaPe
            Nov 21 '16 at 11:10





            Thank you. This is a perfect example for my case. Now i can clean up my workaround to get back a clean code!

            – kaaPe
            Nov 21 '16 at 11:10













            0














            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 ?? "")")
            }
            }





            share|improve this answer




























              0














              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 ?? "")")
              }
              }





              share|improve this answer


























                0












                0








                0







                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 ?? "")")
                }
                }





                share|improve this answer













                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 ?? "")")
                }
                }






                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Nov 20 '18 at 22:52









                Alexander LubyaginAlexander Lubyagin

                513517




                513517






























                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Stack Overflow!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f40690726%2fswift-multiple-chain-http-request-with-loop%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    Guess what letter conforming each word

                    Port of Spain

                    Run scheduled task as local user group (not BUILTIN)