Swift 组合处理不同类型的发布服务器
我真的是一个新的结合,我被困在这个问题。我有一个基本的注册表单,如果一切正常,它返回200代码的空响应,如果表单有一些注册失败,它返回442代码 这是可以处理空响应的代码,工作正常Swift 组合处理不同类型的发布服务器,swift,networking,combine,Swift,Networking,Combine,我真的是一个新的结合,我被困在这个问题。我有一个基本的注册表单,如果一切正常,它返回200代码的空响应,如果表单有一些注册失败,它返回442代码 这是可以处理空响应的代码,工作正常 extension Route where ResultType: EmptyResult { func emptyResult() -> AnyPublisher<Void, APIError> { return URLSession.shared.dataTaskPubl
extension Route where ResultType: EmptyResult {
func emptyResult() -> AnyPublisher<Void, APIError> {
return URLSession.shared.dataTaskPublisher(for: urlRequest)
.print("EMPTY RESULT")
.tryMap { data, response in
guard let httpResponse = response as? HTTPURLResponse else { throw APIError.nonHttpResponse(description: "Not http resp") }
let statusCode = httpResponse.statusCode
guard (200..<300).contains(statusCode) else { throw APIError.nonHttpResponse(description: "bad response")
}
return Void()
}.mapError { error in
print("Error \(error)")
return .network(description: error.localizedDescription)
}
.eraseToAnyPublisher()
}
}
我的网络电话:
API.registration(name: name, email: email, password: password, schoolID: selectedSchool?.id ?? 0)
.print("Registration")
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { (completion) in
switch completion {
case let .failure(error):
print("ERROR \(error)")
case .finished: break
}
}, receiveValue: { value in
print(value)
})
.store(in: &disposables)
因此,您有一个网络请求,如果请求成功,它将返回一个200响应和一个空正文,而如果表单错误,它将返回一个特定的状态代码和响应中的一个错误 我建议将
发布者的输出类型保持为无效
,但是,如果出现表单错误,请解码错误并将其作为APIRROR
的一部分抛出
struct LoginError: Decodable {
let usernameError: String
let emailError: String
}
enum APIError: Error {
case failureStatus(code: Int)
case login(LoginError)
case nonHttpResponse(description: String)
case network(Error)
}
func emptyResult() -> AnyPublisher<Void, APIError> {
return URLSession.shared.dataTaskPublisher(for: urlRequest)
.print("EMPTY RESULT")
.tryMap { data, response in
guard let httpResponse = response as? HTTPURLResponse else { throw APIError.nonHttpResponse(description: "Not http response") }
let statusCode = httpResponse.statusCode
guard (200..<300).contains(statusCode) else {
if statusCode == 442 {
let loginError = try JSONDecoder().decode(LoginError.self, from: data)
throw APIError.login(loginError)
} else {
throw APIError.failureStatus(code: statusCode)
}
}
return Void()
}.mapError { error in
switch error {
case let apiError as APIError:
return apiError
default:
return .network(error)
}
}
.eraseToAnyPublisher()
}
那么,您的网络请求是否总是返回一个CustomError
作为响应?不总是,只有在存在表单验证问题时才返回。如果没有表单验证问题,我将得到200个代码的空响应!然而,还有一个问题。在我的ViewModel中,我看到只有网络错误被抛出,而不是我们解析的状态代码为442时抛出的错误。可能.mapError覆盖了之前抛出的错误?@asmartyn同样,如果您的实际网络请求失败,那么是,dataTaskPublisher
将失败并出现错误,因此将调用mapError
,而不是tryMap
。但是,dataTaskPublisher
出错意味着请求没有响应,因为请求本身失败了。您需要调查请求失败的原因。这很奇怪,因为如果语句和AuthError被成功解码,我可以在状态代码中打印数据,所以似乎mapError还是被调用了。从日志中我看到空结果:receive cancel,可能是这样?@AsMartynas实际上是我的错,如果您在tryMap
中抛出错误,该错误将传播到mapError
,并且不会立即使发布者失败。检查我的更新答案,现在mapError
应该能够正确处理从tryMap
内部抛出的错误。@AsMartynas您只需在接收器中切换错误
。检查我的最新答案。
struct LoginError: Decodable {
let usernameError: String
let emailError: String
}
enum APIError: Error {
case failureStatus(code: Int)
case login(LoginError)
case nonHttpResponse(description: String)
case network(Error)
}
func emptyResult() -> AnyPublisher<Void, APIError> {
return URLSession.shared.dataTaskPublisher(for: urlRequest)
.print("EMPTY RESULT")
.tryMap { data, response in
guard let httpResponse = response as? HTTPURLResponse else { throw APIError.nonHttpResponse(description: "Not http response") }
let statusCode = httpResponse.statusCode
guard (200..<300).contains(statusCode) else {
if statusCode == 442 {
let loginError = try JSONDecoder().decode(LoginError.self, from: data)
throw APIError.login(loginError)
} else {
throw APIError.failureStatus(code: statusCode)
}
}
return Void()
}.mapError { error in
switch error {
case let apiError as APIError:
return apiError
default:
return .network(error)
}
}
.eraseToAnyPublisher()
}
API.registration(name: name, email: email, password: password, schoolID: selectedSchool?.id ?? 0)
.print("Registration")
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { (completion) in
switch completion {
case let .failure(error):
switch error {
case .login(let loginError):
print("Login failed, \(loginError.emailError), \(loginError.usernameError)")
default:
print(error)
}
case .finished: break
}
}, receiveValue: { value in
print(value)
})
.store(in: &disposables)