Swift - multiple Chain http request with loop
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:
Upload a image to a server
- Response = XML Format with a Task ID
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
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
add a comment |
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:
Upload a image to a server
- Response = XML Format with a Task ID
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
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
add a comment |
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:
Upload a image to a server
- Response = XML Format with a Task ID
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
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
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:
Upload a image to a server
- Response = XML Format with a Task ID
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
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
ios swift alamofire promisekit
edited May 23 '17 at 12:30
Community♦
11
11
asked Nov 19 '16 at 8:23
kaaPekaaPe
133
133
add a comment |
add a comment |
2 Answers
2
active
oldest
votes
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 aData
. 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.
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
add a comment |
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 ?? "")")
}
}
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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 aData
. 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.
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
add a comment |
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 aData
. 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.
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
add a comment |
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 aData
. 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.
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 aData
. 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.
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
add a comment |
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
add a comment |
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 ?? "")")
}
}
add a comment |
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 ?? "")")
}
}
add a comment |
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 ?? "")")
}
}
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 ?? "")")
}
}
answered Nov 20 '18 at 22:52
Alexander LubyaginAlexander Lubyagin
513517
513517
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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