Reputation: 2610
I have a iOS application that use Alamofire to make URL requests. I sometimes find the requests arriving in the wrong order when there is very little time between them. This has to do with the nature of async requests as I understand. Is there any way to guarantee the correct order of the requests? I've been thinking you could wait for each request to finish because you have a completion handler or maybe you could handle this at the server side with a timestamp on each request so the server can discard requests that has a lower timestamp. I don't know what's the best solution though.
My code so far:
Alamofire.request(
defaults.string(forKey: "APIurl")! + path,
method: httpMethod,
parameters: parameters,
encoding: JSONEncoding.default,
headers: headers).responseJSON
{ response in
// Check if the request was successful
var success = false
if (response.result.isSuccess) {
let statusCode = response.response!.statusCode
if (statusCode == 200) {
success = true
} else {
showAlert("COULD_NOT_COMPLETE_TASK_TITLE", message: "TRY_AGAIN_LATER")
}
}
}
I use sliders to change a value between 0 and 100. So in my case the order of the requests are crucial. Let's say I change a slider from 50 to 60. With async requests it sometimes execute first 60 and then 50. This is an issue as it's sent to my API that way and saves the latest value (in this case 50) in the database even though my desired value was 60.
Upvotes: 0
Views: 2052
Reputation: 159
Well if the order of requests in crucial in your case then you should go for the NSOperationQueue that is the only way to make sure the order of your requests.
Follow this tutorial to have a border idea
Upvotes: 0
Reputation: 16794
If the thread is serial which in your case it is then the order will always be the same as you input it. So calling a few operations asynchronously on a same serial thread will force the operations to preserve that order.
The problem in your case is that you are not calling these operations, Alamofire is calling them. The order is preserved but it depends on when the responses are received and parsed. That means you may have no control over the order of the async operations being called.
You have 2 ways of serializing responses:
You need to wait for each response to be completed before you call the next request. If your responses are standard (all look alike) you only need some manager that will store an array of requests and will not call the new one until the previous one is completed. This might be a bit slow since there is no reason (or at least it seems that way in your case) not to perform the requests at the same time.
Serialize the responses so they are called in the same order as input. This means you call the requests whenever and responses will be called whenever. But once the response is received you will check for other responses being complete and only if they are you will trigger a callback on this one. That would again mean having some manager that serializes there responses.
So for the second one you would need something like:
SerializedRequestManager.performRequest(request, myCallbackClosure)
The manager would then save the request into some array of request wrappers like:
let requestWrapper = RequestWrapper(request, myCallbackClosure, isCompleted: false)
self.requestPool.append(requestWrapper)
AF.performRequest(request, myInternalClosure)
Then on response (myInternalClosure
) you need to set the correct wrapper to have response to true and then flush the responses from the start of the array. All the responses finished then must be removed from the array:
requestWrapper.responseData = data // Same wrapper that was just added into the array
requestWrapper.responseError = error // Same wrapper that was just added into the array
requestWrapper.isCompleted = true
self.flushResponses()
So then flushResponses
:
var newPool = [RequestWrapper]() // This is where all the pending items will stay
var shouldFlushResponse = true // Will be set to false with first request that was not completed
self.requestPool.forEach { wrapper in
if wrapper.isCompleted && shouldFlushResponse {
wrapper.callback(wrapper.responseData, wrapper.responseError) // perform response closure
} else {
shouldFlushResponse = false
newPool.append(wrapper)
}
}
self.requestPool = newPool
But you need to be very careful about the multithreading here. All the operation on the array requestPool
should be done on the same thread but it may be any thread you want.
Upvotes: 0