问题
What is the correct way to alter a Request
performing an asynchronous task before the Request happens?
So any request Rn need to become transparently Tn then Rn.
A little of background here: The Task is a 3rd party SDK that dispatch a Token I need to use as Header for the original request.
My idea is to decorate the Rn, but in doing this I need to convert my Tn task into a Siesta Request
I can chain then.
So I wrapped the Asynchronous Task and chained to my original request.
Thus any Rn
will turn into Tn.chained { .passTo(Rn) }
In that way, this new behaviour is entirely transparent for the whole application.
The problem
Doing this my code end up crashing in a Siesta internal precondition:
precondition(completedValue == nil, "notifyOfCompletion() already called")
In my custom AsyncTaskRequest I collect the callbacks for success, failure, progress etc, in order to trigger them on the main queue when the SDK deliver the Token.
I noticed that removing all the stored callback once they are executed, the crash disappear, but honestly I didn't found the reason why.
I hope there are enough informations for some hints or suggests. Thank you in advance.
回答1:
Yes, implementing Siesta’s Request
interface is no picnic. Others have had exactly the same problem — and luckily Siesta version 1.4 includes a solution.
Documentation for the new feature is still thin. To use the new API, you’ll implement the new RequestDelegate
protocol, and pass your implementation to Resource.prepareRequest(using:)
. That will return a request that you can use in a standard Siesta request chain. The result will look something like this (WARNING – untested code):
struct MyTokenHandlerThingy: RequestDelegate {
// 3rd party SDK glue goes here
}
...
service.configure(…) {
if let authToken = self.authToken {
$0.headers["X-Auth-Token"] = authToken // authToken is an instance var or something
}
$0.decorateRequests {
self.refreshTokenOnAuthFailure(request: $1)
}
}
func refreshTokenOnAuthFailure(request: Request) -> Request {
return request.chained {
guard case .failure(let error) = $0.response, // Did request fail…
error.httpStatusCode == 401 else { // …because of expired token?
return .useThisResponse // If not, use the response we got.
}
return .passTo(
self.refreshAuthToken().chained { // If so, first request a new token, then:
if case .failure = $0.response { // If token request failed…
return .useThisResponse // …report that error.
} else {
return .passTo(request.repeated()) // We have a new token! Repeat the original request.
}
}
)
}
}
func refreshAuthToken() -> Request {
return Request.prepareRequest(using: MyTokenHandlerThingy())
.onSuccess {
self.authToken = $0.jsonDict["token"] as? String // Store the new token, then…
self.invalidateConfiguration() // …make future requests use it
}
}
}
To understand how to implement RequestDelegate
, you best bet for now is to look at the new API docs directly in the code.
Since this is a brand new feature not yet released, I’d greatly appreciate a report on how it works for you and any troubles you encounter.
来源:https://stackoverflow.com/questions/51029172/how-to-decorate-siesta-request-with-an-asynchronous-task