您应该让API错误返回错误对象。
E、 g.你可以:
enum NetworkRequestError: Error {
case api(_ status: Int, _ code: ApiResultCode, _ description: String)
}
把你的回答编码成
enum
打电话
ApiResultCode
就像这样:
enum ApiResultCode {
case invalidAppId
case recordNotFound // just an example
...
case unknown(String)
}
extension ApiResultCode {
static func code(for string: String) -> ApiResultCode {
switch string {
case "invalid_app_id": return .invalidAppId
case "record_not_found": return .recordNotFound
...
default: return .unknown(string)
}
}
}
此枚举允许您检查
message
代码而不是乱扔字符串文字你的代码。
如果你分析一个API错误,你可以返回它。例如。
if responseObject.error {
let error = NetworkRequestError.api(responseObject.status, ApiResultCode.code(for: responseObject.message), responseObject.description)
... now pass this `error`, just like any other `Error` object
}
如果你愿意进行更广泛的重新设计,我个人建议
-
重构
RequestResult
要提取这些单独的错误类型(调用方只想知道它是成功还是失败。。。如果失败了,它应该看看
Error
确定失败原因的对象);
-
但是有这个新的
Result
枚举包括关联值,即
Data
论成功
错误
失败时;以及
-
既然枚举在其关联值中包含了我们需要的内容,那么我们可以完全消除
ReturnedData
.
所以,首先,让我们扩展一下
请求结果
要包含失败时的错误和成功时的有效负载,请执行以下操作:
public enum Result {
case success(Data)
case failure(Error)
}
事实上,现代的惯例是使这个通用的
Result<Data, Error>
使用以下方法:
public enum Result<T, U> {
case success(T)
case failure(U)
}
(Swift 5实际上包含了这个通用的。)
然后我会扩展
ResultError
要同时处理API错误和任何未知错误,请执行以下操作:
enum NetworkRequestError: Error {
case api(_ status: Int, _ code: ApiResultCode, _ description: String)
case unknown(Data?, URLResponse?)
}
所以,做完这些,你可以改变
request
传回来
结果<数据,错误>
:
static func request(urlString: String, parameters: [String: String], completion: @escaping (Result<Data, Error>) -> ()) {
let request = URLRequest(url: URL(string: urlString)!)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let responseData = data, error == nil else {
completion(.failure(error ?? NetworkRequestError.unknown(data, response)))
return
}
completion(.success(responseData))
}
task.resume()
}
然后打电话的人会:
request(...) { result in
switch result {
case .failure(let error):
// do something with `error`
case .success(let data):
// do something with `data`
}
}
它的美丽
结果
泛型是它成为一个可以在整个代码中使用的一致模式。例如,假设有一个方法将解析
Foo
对象不在
数据
那个
请求
返回:
func retrieveFoo(completion: @escaping (Result<Foo, Error>) -> Void) {
request(...) { result in
switch result {
case .failure(let error):
completion(.failure(error))
case .success(let data):
do {
let responseObject = try JSONDecoder().decode(ResponseObject.self, from: data)
if responseObject.error {
completion(.failure(NetworkRequestError.api(responseObject.status, ApiResultCode.code(for: responseObject.message), responseObject.description)))
return
}
let foo = responseObject.foo
completion(.success(foo))
} catch {
completion(.failure(error))
}
}
}
}
或者,如果您想测试特定的API错误,例如。
.recordNotFound
:
retrieveFoo { result in
switch result {
case .failure(NetworkRequestError.api(_, .recordNotFound, _)):
// handle specific ârecord not foundâ error here
case .failure(let error):
// handle all other errors here
case .success(let foo):
// do something with `foo`
}
}