... | @@ -332,129 +332,135 @@ Please use `swift5` generator because Swift 4.x is deprecated. |
... | @@ -332,129 +332,135 @@ Please use `swift5` generator because Swift 4.x is deprecated. |
|
<details>
|
|
<details>
|
|
<summary>Then you subclass URLSessionRequestBuilder and URLSessionDecodableRequestBuilder </summary>
|
|
<summary>Then you subclass URLSessionRequestBuilder and URLSessionDecodableRequestBuilder </summary>
|
|
|
|
|
|
class BearerRequestBuilder<T>: URLSessionRequestBuilder<T> {
|
|
class BearerRequestBuilder<T>: URLSessionRequestBuilder<T> {
|
|
override func execute(_ apiResponseQueue: DispatchQueue = PetstoreClientAPI.apiResponseQueue, _ completion: @escaping (Result<Response<T>, Error>) -> Void) {
|
|
@discardableResult
|
|
|
|
override func execute(_ apiResponseQueue: DispatchQueue = PetstoreClientAPI.apiResponseQueue, _ completion: @escaping (Result<Response<T>, ErrorResponse>) -> Void) -> OpenApiRequestCancellable {
|
|
|
|
|
|
|
|
// Before making the request, we can validate if we have a bearer token to be able to make a request
|
|
|
|
BearerTokenHandler.refreshTokenIfDoesntExist {
|
|
|
|
|
|
// Before making the request, we can validate if we have a bearer token to be able to make a request
|
|
// Here we make the request
|
|
BearerTokenHandler.refreshTokenIfDoesntExist {
|
|
super.execute(apiResponseQueue) { result in
|
|
|
|
|
|
// Here we make the request
|
|
switch result {
|
|
super.execute(apiResponseQueue) { result in
|
|
case .success:
|
|
|
|
// If we got a successful response, we send the response to the completion block
|
|
|
|
completion(result)
|
|
|
|
|
|
switch result {
|
|
case let .failure(error):
|
|
case .success:
|
|
|
|
// If we got a successful response, we send the response to the completion block
|
|
// If we got a failure response, we will analyse the error to see what we should do with it
|
|
completion(result)
|
|
if case let ErrorResponse.error(_, data, response, error) = error {
|
|
|
|
|
|
case let .failure(error):
|
|
|
|
|
|
|
|
// If we got a failure response, we will analyse the error to see what we should do with it
|
|
// If the error is an ErrorResponse.error() we will analyse it to see if it's a 401, and if it's a 401, we will refresh the token and retry the request
|
|
if case let ErrorResponse.error(_, data, response, error) = error {
|
|
BearerTokenHandler.refreshTokenIfUnauthorizedRequestResponse(
|
|
|
|
data: data,
|
|
|
|
response: response,
|
|
|
|
error: error
|
|
|
|
) { wasTokenRefreshed in
|
|
|
|
|
|
// If the error is an ErrorResponse.error() we will analyse it to see if it's a 401, and if it's a 401, we will refresh the token and retry the request
|
|
if wasTokenRefreshed {
|
|
BearerTokenHandler.refreshTokenIfUnauthorizedRequestResponse(
|
|
// If the token was refreshed, it's because it was a 401 error, so we refreshed the token, and we are going to retry the request by calling self.execute()
|
|
data: data,
|
|
self.execute(apiResponseQueue, completion)
|
|
response: response,
|
|
} else {
|
|
error: error
|
|
// If the token was not refreshed, it's because it was not a 401 error, so we send the response to the completion block
|
|
) { wasTokenRefreshed in
|
|
completion(result)
|
|
|
|
|
|
if wasTokenRefreshed {
|
|
|
|
// If the token was refreshed, it's because it was a 401 error, so we refreshed the token, and we are going to retry the request by calling self.execute()
|
|
|
|
self.execute(apiResponseQueue, completion)
|
|
|
|
} else {
|
|
|
|
// If the token was not refreshed, it's because it was not a 401 error, so we send the response to the completion block
|
|
|
|
completion(result)
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
} else {
|
|
|
|
// If it's an unknown error, we send the response to the completion block
|
|
|
|
completion(result)
|
|
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
// If it's an unknown error, we send the response to the completion block
|
|
|
|
completion(result)
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return requestCancellable
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
class BearerDecodableRequestBuilder<T: Decodable>: URLSessionDecodableRequestBuilder<T> {
|
|
class BearerDecodableRequestBuilder<T: Decodable>: URLSessionDecodableRequestBuilder<T> {
|
|
override func execute(_ apiResponseQueue: DispatchQueue = PetstoreClientAPI.apiResponseQueue, _ completion: @escaping (Result<Response<T>, Error>) -> Void) {
|
|
@discardableResult
|
|
|
|
override func execute(_ apiResponseQueue: DispatchQueue = PetstoreClientAPI.apiResponseQueue, _ completion: @escaping (Result<Response<T>, ErrorResponse>) -> Void) -> OpenApiRequestCancellable {
|
|
|
|
// Before making the request, we can validate if we have a bearer token to be able to make a request
|
|
|
|
BearerTokenHandler.refreshTokenIfDoesntExist {
|
|
|
|
|
|
// Before making the request, we can validate if we have a bearer token to be able to make a request
|
|
// Here we make the request
|
|
BearerTokenHandler.refreshTokenIfDoesntExist {
|
|
super.execute(apiResponseQueue) { result in
|
|
|
|
|
|
// Here we make the request
|
|
switch result {
|
|
super.execute(apiResponseQueue) { result in
|
|
case .success:
|
|
|
|
// If we got a successful response, we send the response to the completion block
|
|
|
|
completion(result)
|
|
|
|
|
|
switch result {
|
|
case let .failure(error):
|
|
case .success:
|
|
|
|
// If we got a successful response, we send the response to the completion block
|
|
// If we got a failure response, we will analyse the error to see what we should do with it
|
|
completion(result)
|
|
if case let ErrorResponse.error(_, data, response, error) = error {
|
|
|
|
|
|
case let .failure(error):
|
|
|
|
|
|
|
|
// If we got a failure response, we will analyse the error to see what we should do with it
|
|
// If the error is an ErrorResponse.error() we will analyse it to see if it's a 401, and if it's a 401, we will refresh the token and retry the request
|
|
if case let ErrorResponse.error(_, data, response, error) = error {
|
|
BearerTokenHandler.refreshTokenIfUnauthorizedRequestResponse(
|
|
|
|
data: data,
|
|
|
|
response: response,
|
|
|
|
error: error
|
|
|
|
) { wasTokenRefreshed in
|
|
|
|
|
|
// If the error is an ErrorResponse.error() we will analyse it to see if it's a 401, and if it's a 401, we will refresh the token and retry the request
|
|
if wasTokenRefreshed {
|
|
BearerTokenHandler.refreshTokenIfUnauthorizedRequestResponse(
|
|
// If the token was refreshed, it's because it was a 401 error, so we refreshed the token, and we are going to retry the request by calling self.execute()
|
|
data: data,
|
|
self.execute(apiResponseQueue, completion)
|
|
response: response,
|
|
} else {
|
|
error: error
|
|
// If the token was not refreshed, it's because it was not a 401 error, so we send the response to the completion block
|
|
) { wasTokenRefreshed in
|
|
completion(result)
|
|
|
|
|
|
if wasTokenRefreshed {
|
|
|
|
// If the token was refreshed, it's because it was a 401 error, so we refreshed the token, and we are going to retry the request by calling self.execute()
|
|
|
|
self.execute(apiResponseQueue, completion)
|
|
|
|
} else {
|
|
|
|
// If the token was not refreshed, it's because it was not a 401 error, so we send the response to the completion block
|
|
|
|
completion(result)
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
} else {
|
|
|
|
// If it's an unknown error, we send the response to the completion block
|
|
|
|
completion(result)
|
|
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
// If it's an unknown error, we send the response to the completion block
|
|
|
|
completion(result)
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return requestCancellable
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
class BearerTokenHandler {
|
|
class BearerTokenHandler {
|
|
private static var bearerToken: String? = nil
|
|
private static var bearerToken: String? = nil
|
|
|
|
|
|
static func refreshTokenIfDoesntExist(completionHandler: @escaping () -> Void) {
|
|
static func refreshTokenIfDoesntExist(completionHandler: @escaping () -> Void) {
|
|
if bearerToken != nil {
|
|
if bearerToken != nil {
|
|
|
|
completionHandler()
|
|
|
|
} else {
|
|
|
|
startRefreshingToken {
|
|
completionHandler()
|
|
completionHandler()
|
|
} else {
|
|
|
|
startRefreshingToken {
|
|
|
|
completionHandler()
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
static func refreshTokenIfUnauthorizedRequestResponse(data: Data?, response: URLResponse?, error: Error?, completionHandler: @escaping (Bool) -> Void) {
|
|
|
|
if let response = response as? HTTPURLResponse, response.statusCode == 401 {
|
|
static func refreshTokenIfUnauthorizedRequestResponse(data: Data?, response: URLResponse?, error: Error?, completionHandler: @escaping (Bool) -> Void) {
|
|
startRefreshingToken {
|
|
if let response = response as? HTTPURLResponse, response.statusCode == 401 {
|
|
completionHandler(true)
|
|
startRefreshingToken {
|
|
}
|
|
completionHandler(true)
|
|
} else {
|
|
|
|
completionHandler(false)
|
|
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
completionHandler(false)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static func startRefreshingToken(completionHandler: @escaping () -> Void) {
|
|
|
|
// Get a bearer token
|
|
|
|
let dummyBearerToken = "..."
|
|
|
|
|
|
private static func startRefreshingToken(completionHandler: @escaping () -> Void) {
|
|
bearerToken = dummyBearerToken
|
|
// Get a bearer token
|
|
PetstoreClientAPI.customHeaders["Authorization"] = "Bearer \(dummyBearerToken)"
|
|
let dummyBearerToken = "..."
|
|
|
|
|
|
|
|
bearerToken = dummyBearerToken
|
|
|
|
PetstoreClientAPI.customHeaders["Authorization"] = "Bearer \(dummyBearerToken)"
|
|
|
|
|
|
|
|
completionHandler()
|
|
completionHandler()
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
</details>
|
|
</details>
|
|
|
|
|
|
Then you assign the `BearerRequestBuilderFactory` to the property requestBuilderFactory in the APIs.swift file.
|
|
Then you assign the `BearerRequestBuilderFactory` to the property requestBuilderFactory in the APIs.swift file.
|
... | | ... | |