Alamofire中把整个网络请求从建立请求到响应完成中可能出现的错误,所有封装为AFError枚举对象,而且该枚举的值,大部分都为带参枚举,甚至带有多级参数,在AFError内部定义了多个子枚举来表明不一样类型的错误,并扩展AFError,提供了许多简单操做的方法与属性,整个AFError文件就有800+行的代码,不过总体并不难理解。swift
AFError枚举所列出的错误类型,大部分类型都带有参数,用来描述详细的二级错误类型服务器
/// 上传请求,在调用createUploadable()方法建立Uploadable的时候失败, 枚举参数为错误缘由
case createUploadableFailed(error: Error)
/// URLRequestConvertible协议对象调用asURLRequest()方法建立URLRequest对象时失败, 参数为错误缘由
case createURLRequestFailed(error: Error)
/// SessionDelegate在处理下载请求时, 试图把下载文件从临时路径移动到目标路径时失败, 参数为错误缘由, 源路径, 目标路径
case downloadedFileMoveFailed(error: Error, source: URL, destination: URL)
/// 请求取消
case explicitlyCancelled
/// URLConvertible对象调用asURL()方法建立URL对象失败
case invalidURL(url: URLConvertible)
/// 多表单参数编码失败, 参数为编码失败具体的缘由, 是个定义的枚举类型
case multipartEncodingFailed(reason: MultipartEncodingFailureReason)
/// 请求参数使用parameterEncoding编码失败, 参数为编码失败具体的缘由, 也是个定义的枚举类型
case parameterEncodingFailed(reason: ParameterEncodingFailureReason)
/// 请求参数使用parameterEncoder编码失败, 参数为编码失败具体的缘由, 也是个定义的枚举类型
case parameterEncoderFailed(reason: ParameterEncoderFailureReason)
/// RequestAdapter请求适配器在预处理请求时出错, 参数为错误缘由
case requestAdaptationFailed(error: Error)
/// RequestRetrier请求重试器在处理请求行为时出错, 抛出的异常, 参数为重试时出错的缘由, 请求失败的原始缘由
case requestRetryFailed(retryError: Error, originalError: Error)
/// 若定义了响应有效性校验, 未能经过校验时抛出该错误, 参数为未能经过校验的具体缘由
case responseValidationFailed(reason: ResponseValidationFailureReason)
/// 响应数据序列化时出错, 参数为序列化时出错的具体缘由
case responseSerializationFailed(reason: ResponseSerializationFailureReason)
/// 收到服务器认证处理时, 认证失败会抛出该错误, 参数为认证失败的具体缘由
case serverTrustEvaluationFailed(reason: ServerTrustFailureReason)
/// 请求还没落地的时候, Session释放了, 会抛出该错误
case sessionDeinitialized
/// URLSession出错了, 会抛出该错误, 并带上URLSession出错的缘由
case sessionInvalidated(error: Error?)
/// URLSessionTask返回错误
case sessionTaskFailed(error: Error)
/// URLRequest有效性校验出错, 只有一个错误可能: get请求,带有了body数据
case urlRequestValidationFailed(reason: URLRequestValidationFailureReason)
复制代码
错误枚举中,有好几个错误的参数有自定义的具体的错误缘由枚举,错误处理时能够对这些缘由进行具体细化处理。markdown
定义了多表单编码失败时的具体缘由:网络
/// The underlying reason the `.multipartEncodingFailed` error occurred.
public enum MultipartEncodingFailureReason {
/// The `fileURL` provided for reading an encodable body part isn't a file `URL`.
case bodyPartURLInvalid(url: URL)
/// The filename of the `fileURL` provided has either an empty `lastPathComponent` or `pathExtension.
case bodyPartFilenameInvalid(in: URL)
/// The file at the `fileURL` provided was not reachable.
case bodyPartFileNotReachable(at: URL)
/// Attempting to check the reachability of the `fileURL` provided threw an error.
case bodyPartFileNotReachableWithError(atURL: URL, error: Error)
/// The file at the `fileURL` provided is actually a directory.
case bodyPartFileIsDirectory(at: URL)
/// The size of the file at the `fileURL` provided was not returned by the system.
case bodyPartFileSizeNotAvailable(at: URL)
/// The attempt to find the size of the file at the `fileURL` provided threw an error.
case bodyPartFileSizeQueryFailedWithError(forURL: URL, error: Error)
/// An `InputStream` could not be created for the provided `fileURL`.
case bodyPartInputStreamCreationFailed(for: URL)
/// An `OutputStream` could not be created when attempting to write the encoded data to disk.
case outputStreamCreationFailed(for: URL)
/// The encoded body data could not be written to disk because a file already exists at the provided `fileURL`.
case outputStreamFileAlreadyExists(at: URL)
/// The `fileURL` provided for writing the encoded body data to disk is not a file `URL`.
case outputStreamURLInvalid(url: URL)
/// The attempt to write the encoded body data to disk failed with an underlying error.
case outputStreamWriteFailed(error: Error)
/// The attempt to read an encoded body part `InputStream` failed with underlying system error.
case inputStreamReadFailed(error: Error)
}
复制代码
并扩展了该枚举,添加了能够快速获取请求url与潜在Error对象的计算属性:session
extension AFError.MultipartEncodingFailureReason {
var url: URL? {
switch self {
case let .bodyPartURLInvalid(url),
let .bodyPartFilenameInvalid(url),
let .bodyPartFileNotReachable(url),
let .bodyPartFileIsDirectory(url),
let .bodyPartFileSizeNotAvailable(url),
let .bodyPartInputStreamCreationFailed(url),
let .outputStreamCreationFailed(url),
let .outputStreamFileAlreadyExists(url),
let .outputStreamURLInvalid(url),
let .bodyPartFileNotReachableWithError(url, _),
let .bodyPartFileSizeQueryFailedWithError(url, _):
return url
case .outputStreamWriteFailed,
.inputStreamReadFailed:
return nil
}
}
var underlyingError: Error? {
switch self {
case let .bodyPartFileNotReachableWithError(_, error),
let .bodyPartFileSizeQueryFailedWithError(_, error),
let .outputStreamWriteFailed(error),
let .inputStreamReadFailed(error):
return error
case .bodyPartURLInvalid,
.bodyPartFilenameInvalid,
.bodyPartFileNotReachable,
.bodyPartFileIsDirectory,
.bodyPartFileSizeNotAvailable,
.bodyPartInputStreamCreationFailed,
.outputStreamCreationFailed,
.outputStreamFileAlreadyExists,
.outputStreamURLInvalid:
return nil
}
}
}
复制代码
定义了请求参数使用ParameterEncoding协议对象编码失败时的缘由,一样扩展了枚举用来快速获取潜在Error参数闭包
/// The underlying reason the `.parameterEncodingFailed` error occurred.
public enum ParameterEncodingFailureReason {
/// The `URLRequest` did not have a `URL` to encode.
case missingURL
/// JSON serialization failed with an underlying system error during the encoding process.
case jsonEncodingFailed(error: Error)
/// Custom parameter encoding failed due to the associated `Error`.
case customEncodingFailed(error: Error)
}
复制代码
定义了请求参数使用ParameterEncoder协议对象编码失败时的缘由,相似上的ParameterEncodingFailureReasonapp
public enum ParameterEncoderFailureReason {
/// Possible missing components.
public enum RequiredComponent {
/// The `URL` was missing or unable to be extracted from the passed `URLRequest` or during encoding.
case url
/// The `HTTPMethod` could not be extracted from the passed `URLRequest`.
case httpMethod(rawValue: String)
}
/// A `RequiredComponent` was missing during encoding.
case missingRequiredComponent(RequiredComponent)
/// The underlying encoder failed with the associated error.
case encoderFailed(error: Error)
}
复制代码
定义了响应数据的有效性检测失败的具体缘由ide
/// The underlying reason the `.responseValidationFailed` error occurred.
public enum ResponseValidationFailureReason {
/// The data file containing the server response did not exist.
case dataFileNil
/// The data file containing the server response at the associated `URL` could not be read.
case dataFileReadFailed(at: URL)
/// The response did not contain a `Content-Type` and the `acceptableContentTypes` provided did not contain a
/// wildcard type.
case missingContentType(acceptableContentTypes: [String])
/// The response `Content-Type` did not match any type in the provided `acceptableContentTypes`.
case unacceptableContentType(acceptableContentTypes: [String], responseContentType: String)
/// The response status code was not acceptable.
case unacceptableStatusCode(code: Int)
/// Custom response validation failed due to the associated `Error`.
case customValidationFailed(error: Error)
}
复制代码
并扩展添加了便捷计算属性,能够快速得到请求接受的contentType,响应返回的contentType,响应状态码,潜在Error对象等工具
extension AFError.ResponseValidationFailureReason {
var acceptableContentTypes: [String]? {
switch self {
case let .missingContentType(types),
let .unacceptableContentType(types, _):
return types
case .dataFileNil,
.dataFileReadFailed,
.unacceptableStatusCode,
.customValidationFailed:
return nil
}
}
var responseContentType: String? {
switch self {
case let .unacceptableContentType(_, responseType):
return responseType
case .dataFileNil,
.dataFileReadFailed,
.missingContentType,
.unacceptableStatusCode,
.customValidationFailed:
return nil
}
}
var responseCode: Int? {
switch self {
case let .unacceptableStatusCode(code):
return code
case .dataFileNil,
.dataFileReadFailed,
.missingContentType,
.unacceptableContentType,
.customValidationFailed:
return nil
}
}
var underlyingError: Error? {
switch self {
case let .customValidationFailed(error):
return error
case .dataFileNil,
.dataFileReadFailed,
.missingContentType,
.unacceptableContentType,
.unacceptableStatusCode:
return nil
}
}
}
复制代码
定义了响应序列化失败的具体缘由,包括序列化为string、json、decodable这三种基本类型失败的缘由或者是序列化为用户自定义类型失败的缘由。
/// The underlying reason the response serialization error occurred.
public enum ResponseSerializationFailureReason {
/// The server response contained no data or the data was zero length.
case inputDataNilOrZeroLength
/// The file containing the server response did not exist.
case inputFileNil
/// The file containing the server response could not be read from the associated `URL`.
case inputFileReadFailed(at: URL)
/// String serialization failed using the provided `String.Encoding`.
case stringSerializationFailed(encoding: String.Encoding)
/// JSON serialization failed with an underlying system error.
case jsonSerializationFailed(error: Error)
/// A `DataDecoder` failed to decode the response due to the associated `Error`.
case decodingFailed(error: Error)
/// A custom response serializer failed due to the associated `Error`.
case customSerializationFailed(error: Error)
/// Generic serialization failed for an empty response that wasn't type `Empty` but instead the associated type.
case invalidEmptyResponse(type: String)
}
复制代码
扩展添加了获取序列化为string出错时使用的字符串编码,以及潜在的Error对象
extension AFError.ResponseSerializationFailureReason {
var failedStringEncoding: String.Encoding? {
switch self {
case let .stringSerializationFailed(encoding):
return encoding
case .inputDataNilOrZeroLength,
.inputFileNil,
.inputFileReadFailed(_),
.jsonSerializationFailed(_),
.decodingFailed(_),
.customSerializationFailed(_),
.invalidEmptyResponse:
return nil
}
}
var underlyingError: Error? {
switch self {
case let .jsonSerializationFailed(error),
let .decodingFailed(error),
let .customSerializationFailed(error):
return error
case .inputDataNilOrZeroLength,
.inputFileNil,
.inputFileReadFailed,
.stringSerializationFailed,
.invalidEmptyResponse:
return nil
}
}
}
复制代码
定义了服务器认证失败的具体缘由,内部定义了服务器认证信息的结构体,用来做为错误具体缘由参数返回
/// Underlying reason a server trust evaluation error occurred.
public enum ServerTrustFailureReason {
/// The output of a server trust evaluation.
public struct Output {
/// The host for which the evaluation was performed.
public let host: String
/// The `SecTrust` value which was evaluated.
public let trust: SecTrust
/// The `OSStatus` of evaluation operation.
public let status: OSStatus
/// The result of the evaluation operation.
public let result: SecTrustResultType
/// Creates an `Output` value from the provided values.
init(_ host: String, _ trust: SecTrust, _ status: OSStatus, _ result: SecTrustResultType) {
self.host = host
self.trust = trust
self.status = status
self.result = result
}
}
/// No `ServerTrustEvaluator` was found for the associated host.
case noRequiredEvaluator(host: String)
/// No certificates were found with which to perform the trust evaluation.
case noCertificatesFound
/// No public keys were found with which to perform the trust evaluation.
case noPublicKeysFound
/// During evaluation, application of the associated `SecPolicy` failed.
case policyApplicationFailed(trust: SecTrust, policy: SecPolicy, status: OSStatus)
/// During evaluation, setting the associated anchor certificates failed.
case settingAnchorCertificatesFailed(status: OSStatus, certificates: [SecCertificate])
/// During evaluation, creation of the revocation policy failed.
case revocationPolicyCreationFailed
/// `SecTrust` evaluation failed with the associated `Error`, if one was produced.
case trustEvaluationFailed(error: Error?)
/// Default evaluation failed with the associated `Output`.
case defaultEvaluationFailed(output: Output)
/// Host validation failed with the associated `Output`.
case hostValidationFailed(output: Output)
/// Revocation check failed with the associated `Output` and options.
case revocationCheckFailed(output: Output, options: RevocationTrustEvaluator.Options)
/// Certificate pinning failed.
case certificatePinningFailed(host: String, trust: SecTrust, pinnedCertificates: [SecCertificate], serverCertificates: [SecCertificate])
/// Public key pinning failed.
case publicKeyPinningFailed(host: String, trust: SecTrust, pinnedKeys: [SecKey], serverKeys: [SecKey])
/// Custom server trust evaluation failed due to the associated `Error`.
case customEvaluationFailed(error: Error)
}
复制代码
扩展添加了快速获取服务器认证信息的方法,以及可能的Error数据
extension AFError.ServerTrustFailureReason {
var output: AFError.ServerTrustFailureReason.Output? {
switch self {
case let .defaultEvaluationFailed(output),
let .hostValidationFailed(output),
let .revocationCheckFailed(output, _):
return output
case .noRequiredEvaluator,
.noCertificatesFound,
.noPublicKeysFound,
.policyApplicationFailed,
.settingAnchorCertificatesFailed,
.revocationPolicyCreationFailed,
.trustEvaluationFailed,
.certificatePinningFailed,
.publicKeyPinningFailed,
.customEvaluationFailed:
return nil
}
}
var underlyingError: Error? {
switch self {
case let .customEvaluationFailed(error):
return error
case let .trustEvaluationFailed(error):
return error
case .noRequiredEvaluator,
.noCertificatesFound,
.noPublicKeysFound,
.policyApplicationFailed,
.settingAnchorCertificatesFailed,
.revocationPolicyCreationFailed,
.defaultEvaluationFailed,
.hostValidationFailed,
.revocationCheckFailed,
.certificatePinningFailed,
.publicKeyPinningFailed:
return nil
}
}
}
复制代码
定义了请求检测失败的具体错误,只有一个可能:get请求带了body参数,就会抛出该错误,并把body的data返回
/// The underlying reason the `.urlRequestValidationFailed`
public enum URLRequestValidationFailureReason {
/// URLRequest with GET method had body data.
case bodyDataInGETRequest(Data)
}
复制代码
AFError中定义了不少简化操做的工具扩展
extension Error {
/// 返回可选类型的AFError, 若self是AFError类型, 就会返回可选的对象, 不然返回nil
public var asAFError: AFError? {
self as? AFError
}
/// 返回不可选的AFError对象, 若self不是AFError类型, 使用参数抛出异常, 注意message是个自动闭包
public func asAFError(orFailWith message: @autoclosure () -> String, file: StaticString = #file, line: UInt = #line) -> AFError {
guard let afError = self as? AFError else {
fatalError(message(), file: file, line: line)
}
return afError
}
/// 返回不可选的AFError对象, 若self不是AFError类型, 就会使用参数自动闭包来建立一个默认的AFError对象
func asAFError(or defaultAFError: @autoclosure () -> AFError) -> AFError {
self as? AFError ?? defaultAFError()
}
}
复制代码
AFError扩展定义了许多isXXXError的Bool的计算属性,用来快速判断错误类型
AFError扩展添加能够返回各类参数类型的计算属性,这些属性返回的都是可选类型,只有类型正确时,才会返回对应的可选类型的参数,不然会返回nil。另外对错误具体缘由的枚举均扩展添加了便捷的属性获取
AFError扩展实现了LocalizedError协议,添加了errorDescription属性返回英文错误描述字符串。内部的错误缘由的枚举,所有都扩展添加localizedDescription属性,用来返回错误的文字描述信息,
在使用Alamofire时,可抛出异常的方法,可直接do-catch来捕捉抛出的类型,在使用asAFError转换成AFError类型,而后处理具体的错误类型便可。