如果之前的请求未经授权,如何执行swift合并请求



我的起点是一个类似于这里的foo()示例的方法。我正在使用retries: 1

func foo() {
// create url ...
let urlRequest = URLRequest(for: url, httpMethod: "POST", resource: nil, token: accessToken)
let combineRequest = CombineRequest(auth: auth)
combineRequest.runWithResponseStatus(urlRequest, checkStatusCode: 201, retries: 1)
.sink { completion in
switch completion {
case .finished:
break
case .failure(let error):
self.toasts.insert(ToastText(type: .error, text: Text("(error.localizedDescription)")), at: 0)
self.clearToasts()
}
} receiveValue: { _ in
// successful
}
.store(in: &cancellables)
}

所以这是一个POST组合请求(runWithResponseStatus()(。当我得到一个401 - unauthorized时,我想执行另一个合并请求(refresh()(。

  1. 如果refresh()成功,它应该执行auth.setAccessToken(to: newAccessToken.token),并重试以前调用的(runWithResponseStatus()(方法一次
  2. 现在,在.failure上,我正在执行print("(error.localizedDescription)"),但我想将该错误返回到链中,以便它最终以foo()方法结束,在那里我可以将它放入toast数组

有人知道如何实现这两点吗?如果可能的话,还知道如何利用联合收割机的力量,让它更清洁吗?

struct CombineRequest {

var auth: Auth
var jsonDecoder: JSONDecoder = JSONDecoder(dateFormatter: .isoDateFormatter)
func runWithResponseStatus(_ request: URLRequest, checkStatusCode statusCode: Int = 200, queue: DispatchQueue = .main, retries: Int = 0) -> AnyPublisher<(data: Data, response: URLResponse), Error> {

var cancellable = Set<AnyCancellable>()

return URLSession.shared.dataTaskPublisher(for: request)
.retry(retries)
.tryMap { output in
if let response = output.response as? HTTPURLResponse, response.statusCode != statusCode {
switch response.statusCode {
case 401:
try refresh(decodingType: AccessData.self)
.sink { completion in
switch completion {
case .finished:
break
case .failure(let error):
print("(error.localizedDescription)")
}
} receiveValue: { newAccessToken in
auth.setAccessToken(to: newAccessToken.token)
}
.store(in: &cancellable)
case 400:
throw ResourceError.internalServerError("General.error.general")
default:
throw ResourceError.httpStatus(response.statusCode)
}
}
return output
}
.receive(on: queue)
.eraseToAnyPublisher()
}

private func refresh<DecodingType>(checkStatusCode statusCode: Int = 200, queue: DispatchQueue = .main, decodingType: DecodingType.Type) throws -> AnyPublisher<DecodingType, Error> where DecodingType: Decodable {

// prepare all properties ...

return URLSession.shared.dataTaskPublisher(for: refreshRequest)
.tryMap { output in
if let response = output.response as? HTTPURLResponse, response.statusCode != statusCode {
switch response.statusCode {
case 401:
auth.loggedOut()
case 400:
throw ResourceError.internalServerError("General.error.general")
default:
auth.loggedOut()
throw ResourceError.httpStatus(response.statusCode)
}
}
return output.data
}
.decode(type: DecodingType.self, decoder: jsonDecoder)
.receive(on: queue)
.eraseToAnyPublisher()
}
}

我相信你可以catchreplaceError。请尝试阅读以下内容:https://www.donnywals.com/catch-vs-replaceerror-in-combine/

最新更新