Alamofire源碼學習(十四): AFError

往期導航:

Alamofire源碼學習目錄合集json

簡介

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

1.MultipartEncodingFailureReason:

定義了多表單編碼失敗時的具體緣由:網絡

/// 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
        }
    }
}
複製代碼

2.ParameterEncodingFailureReason

定義了請求參數使用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)
    }
複製代碼

3.ParameterEncoderFailureReason

定義了請求參數使用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)
    }
複製代碼

4.ResponseValidationFailureReason

定義了響應數據的有效性檢測失敗的具體緣由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
        }
    }
}
複製代碼

5.ResponseSerializationFailureReason

定義了響應序列化失敗的具體緣由,包括序列化爲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
        }
    }
}
複製代碼

6.ServerTrustFailureReason

定義了服務器認證失敗的具體緣由,內部定義了服務器認證信息的結構體,用來做爲錯誤具體緣由參數返回

/// 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
        }
    }
}
複製代碼

7.URLRequestValidationFailureReason

定義了請求檢測失敗的具體錯誤,只有一個可能:get請求帶了body參數,就會拋出該錯誤,並把body的data返回

/// The underlying reason the `.urlRequestValidationFailed`
    public enum URLRequestValidationFailureReason {
        /// URLRequest with GET method had body data.
        case bodyDataInGETRequest(Data)
    }
複製代碼

工具擴展

AFError中定義了不少簡化操做的工具擴展

1.Error擴展添加快速轉換成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()
    }
}
複製代碼

2.便捷Bool值

AFError擴展定義了許多isXXXError的Bool的計算屬性,用來快速判斷錯誤類型

3.便捷屬性獲取

AFError擴展添加能夠返回各類參數類型的計算屬性,這些屬性返回的都是可選類型,只有類型正確時,纔會返回對應的可選類型的參數,不然會返回nil。另外對錯誤具體緣由的枚舉均擴展添加了便捷的屬性獲取

4.錯誤描述擴展

AFError擴展實現了LocalizedError協議,添加了errorDescription屬性返回英文錯誤描述字符串。內部的錯誤緣由的枚舉,所有都擴展添加localizedDescription屬性,用來返回錯誤的文字描述信息,

使用

在使用Alamofire時,可拋出異常的方法,可直接do-catch來捕捉拋出的類型,在使用asAFError轉換成AFError類型,而後處理具體的錯誤類型便可。

相關文章
相關標籤/搜索