Alamofire源碼學習(十七): 工具擴展相關

這是我參與更文挑戰的第1天,活動詳情查看: 更文挑戰html

往期導航:

Alamofire源碼學習目錄合集swift

簡介

Alamofire中對請求相關類型都經過擴展的方式添加了許多方法,這些方法有Alamofire內部業務相關,也有一些輔助工具功能的做用,看清楚後有助於更好的理解Alamofire的架構設計。api

相關文件

AlamofireExtended.swift //Alamofire擴展包裹器,用來對系統類進行擴展時包裹使用,避免方法入侵
Notifications.swift //通知相關工具類定義與擴展,定義了Alamofire中用到的各類通知以及相關通知方法,還定義了一個事件監視器來負責發送這些通知
Validation.swift //擴展了Request及其子類,用來檢測響應的有效性
Combine.swift //Swift中Combine框架的相關擴展
OperationQueue+Alamofire.swift //OP隊列擴展,快速初始化使用
StringEncoding+Alamofire.swift //文本編碼類型轉換擴展
Result+Alamofire.swift //快速處理Result內部類型以及相關斷定
URLSessionConfiguration+Alamofire.swift //快速建立默認配置
DispatchQueue+Alamofire.swift //GCD隊列擴展,快速延遲執行閉包使用
複製代碼

AlamofireExtended

擴展包裹器,基於Swift的泛型擴展約束實現的爲須要擴展的類自由添加方法,同時不對原類型進行過分的方法污染。markdown

當咱們須要爲某個已存在的類添加一個新的方法時,swift下有兩種方法可選:閉包

  1. 直接添加全局方法
  2. 擴展該類添加方法

這兩種方法都能實現爲該類添加新的方法,可是1是全局有效,須要經過方法調用的形式來使用,2的話,會有個問題:擴展的方法,會直接做用在該類型上,整個工程中均可以直接訪問到該方法,好比咱們爲UIButton添加了以下三個方法:架構

extension UIButton {
    func atap1() {}
    func atap2() {}
    func atap3() {}
}
複製代碼

那麼在全部能訪問到該擴展的地方,就能夠調用這些方法: image.png 問題出現了:這三個方法直接出如今button可調用的方法提示中,加入我添加了100個擴展方法,而這些方法都是強業務相關的,那麼這種擴展方式就會致使在全部的button在要調用方法的時候,都能看到這些方法提示,一方面會給調用者形成困惑,另外一方面會存在同名的風險。這就是方法污染。併發

爲了解決上面問題,咱們就可使用這樣的思路來解決:app

  • 不直接對UIButton進行擴展方法,而是把button用一個struct包裹起來
  • 接着對這個struct添加方法
  • 調用方法時,咱們須要對button擴展一個方法,來獲取到這個包裹着button的struct,而後使用這個struct來調用這些方法:
struct ButtonWrapper {
    let btn: UIButton
    
    func atap1() {}
    func atap2() {}
    func atap3() {}
}

extension UIButton {
    var btnWrapper: ButtonWrapper {
        return ButtonWrapper.init(btn: self)
    }
}

func test() {
    
    let btn: UIButton!
    
    btn.btnWrapper.atap1()
    
    
}
複製代碼

能夠看到,這樣的話,咱們想要爲button添加的方法就不會出如今button的可調用列表內了,button的方法列表中值增長了一個獲取Wrapper的方法,在這以後,咱們無論添加多少個方法,都不會對button形成污染,很好的實現了方法隔離。框架

以上的實現,有很大的侷限性,只能對UIButton進行方法添加,因爲Swift泛型與擴展約束的強大,咱們能夠把ButtonWrapper寫成一個泛型的包裹器,能夠包裹一個任意類型的泛型對象,以後經過擴展該包裹器同時對泛型類型進行約束來自由爲須要的類型進行擴展添加方法。異步

public struct AlamofireExtension<ExtendedType> {
    // 包裹的須要擴展的泛型類型對象
    public private(set) var type: ExtendedType

    public init(_ type: ExtendedType) {
        self.type = type
    }
}
複製代碼

這時候對任意類型添加擴展方法時,就是經過擴展AlamofireExtension,並使用where約束來實現

extension AlamofireExtension where ExtendedType == UIButton {
    func atap1() {}
    func atap2() {}
    func atap3() {}
}

extension UIButton {
    var af: AlamofireExtension<UIButton> {
        return .init(self)
    }
}

extension AlamofireExtension where ExtendedType == UILabel {
    var aText1: String { return "" }
}

extension UILabel {
    var af: AlamofireExtension<UILabel> {
        return .init(self)
    }
}

func test() {
    
    var btn: UIButton!
    btn.af.atap1()
    
    var lbl: UILabel!
    lbl.af.aText1
    
}
複製代碼

可是這時候還有個問題,每次須要對新類型添加方法時,都要對該類型進行擴展,添加af計算屬性來返回AlamofireExtension包裹類型,這個行爲在不一樣類型中高度相似,就可使用泛型協議+擴展的方式,添加默認實現:

// 使用協議+協議擴展來實現自由爲須要擴展的對象添加包裹的能力
public protocol AlamofireExtended {
    // 須要擴展的對象的類型
    associatedtype ExtendedType

    // 靜態(類)包裹器
    static var af: AlamofireExtension<ExtendedType>.Type { get set }
    // 實例變量包裹器
    var af: AlamofireExtension<ExtendedType> { get set }
}

// 擴展協議添加默認實現
extension AlamofireExtended {
    // 靜態(類)包裹器, 包裹的是Type
    public static var af: AlamofireExtension<Self>.Type {
        get { AlamofireExtension<Self>.self }
        set {}
    }

    // 實例包裹器, 包裹的是對象自己
    public var af: AlamofireExtension<Self> {
        get { AlamofireExtension(self) }
        set {}
    }
}
複製代碼

這樣的話,咱們須要對某個類型添加方法是,只要對該類型添加擴展,實現AlamofireExtended協議,便可實現建立包裹器的方法,而後擴展包裹器便可自由爲該類型添加各類方法。

extension AlamofireExtension where ExtendedType == UIButton {
    func atap1() {}
    func atap2() {}
    func atap3() {}
}

extension AlamofireExtension where ExtendedType == UILabel {
    var aText1: String { return "" }
}

extension UIButton: AlamofireExtended {}
extension UILabel: AlamofireExtended {}

func test() {
    
    let btn: UIButton!
    btn.af.atap1()
    
    let lbl: UILabel!
    lbl.af.aText1
    
}
複製代碼

Alamofire內部中,實現了AlamofireExtended擴展的主要有兩個地方:

  1. URLSessionConfiguration
  2. ServerTrustEvaluation中對證書的處理中有不少類型實現了該協議

最後 吹爆Swift!!這個設計至關巧妙,經過調用時先使用***.af的調用,也能夠一眼就看出來後面即將調用的方法,是Alamofire相關的擴展方法。

Notifications

Alamofire中使用了許多同通知,主要是請求狀態變動時發出來的,所以Alamofire中定義了這些通知的名字,以及經過擴展Notification與NotificationCenter來實現帶着Request對象來收發通知使用,同時實現了一個事件監聽器,用來監聽請求過程當中的各個狀態,發送對應的通知。

// 擴展Request添加靜態常量來定義通知名字
// 使用: Request.didResumeNotification
extension Request {
    /// Posted when a `Request` is resumed. The `Notification` contains the resumed `Request`.
    public static let didResumeNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didResume")
    /// Posted when a `Request` is suspended. The `Notification` contains the suspended `Request`.
    public static let didSuspendNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didSuspend")
    /// Posted when a `Request` is cancelled. The `Notification` contains the cancelled `Request`.
    public static let didCancelNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didCancel")
    /// Posted when a `Request` is finished. The `Notification` contains the completed `Request`.
    public static let didFinishNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didFinish")

    /// Posted when a `URLSessionTask` is resumed. The `Notification` contains the `Request` associated with the `URLSessionTask`.
    public static let didResumeTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didResumeTask")
    /// Posted when a `URLSessionTask` is suspended. The `Notification` contains the `Request` associated with the `URLSessionTask`.
    public static let didSuspendTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didSuspendTask")
    /// Posted when a `URLSessionTask` is cancelled. The `Notification` contains the `Request` associated with the `URLSessionTask`.
    public static let didCancelTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didCancelTask")
    /// Posted when a `URLSessionTask` is completed. The `Notification` contains the `Request` associated with the `URLSessionTask`.
    public static let didCompleteTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didCompleteTask")
}

// MARK: -

// 擴展通知對象,快速從userInfo中讀寫Request對象
// 可是竟然是直接擴展, 而不是使用AlamofireExtended來添加
extension Notification {
    // 從userInfo獲取Request對象
    public var request: Request? {
        userInfo?[String.requestKey] as? Request
    }

    // 建立通知對象, 並把Request塞入到userInfo中發出去
    init(name: Notification.Name, request: Request) {
        self.init(name: name, object: nil, userInfo: [String.requestKey: request])
    }
    
    //模擬使用
    func test() {
        var req: Request!
        let noti = Notification.init(name: Request.didResumeNotification, request: req)
        let getReq = noti.request
    }
}

//*****************如下是我的添加的內容*****************//
// 我的以爲, 這種帶有業務相關的擴展方法, 最好使用包裹器進行方法隔離
extension Notification: AlamofireExtended {}
extension AlamofireExtension where ExtendedType == Notification {
    public var request: Request? {
        type.userInfo?[String.requestKey] as? Request
    }

    // 建立通知對象, 並把Request塞入到userInfo中發出去
    static func createNotificationWith(name: Notification.Name, request: Request) -> Notification {
        Notification.init(name: name, object: nil, userInfo: [String.requestKey: request])
    }
    
    //模擬使用
    func test() {
        var req: Request!
        let noti = Notification.af.createNotificationWith(name: Request.didResumeNotification, request: req)
        let getReq = noti.af.request
    }
}
//*****************以上是我的添加的內容*****************//

extension NotificationCenter {
    // 快速發送通知
    func postNotification(named name: Notification.Name, with request: Request) {
        let notification = Notification(name: name, request: request)
        post(notification)
    }
}

extension String {
    // 通知的userInfo中保存Request的key
    fileprivate static let requestKey = "org.alamofire.notification.key.request"
}

// 事件監聽器, 用來在各個階段發送對應通知使用
public final class AlamofireNotifications: EventMonitor {
    public func requestDidResume(_ request: Request) {
        NotificationCenter.default.postNotification(named: Request.didResumeNotification, with: request)
    }

    public func requestDidSuspend(_ request: Request) {
        NotificationCenter.default.postNotification(named: Request.didSuspendNotification, with: request)
    }

    public func requestDidCancel(_ request: Request) {
        NotificationCenter.default.postNotification(named: Request.didCancelNotification, with: request)
    }

    public func requestDidFinish(_ request: Request) {
        NotificationCenter.default.postNotification(named: Request.didFinishNotification, with: request)
    }

    public func request(_ request: Request, didResumeTask task: URLSessionTask) {
        NotificationCenter.default.postNotification(named: Request.didResumeTaskNotification, with: request)
    }

    public func request(_ request: Request, didSuspendTask task: URLSessionTask) {
        NotificationCenter.default.postNotification(named: Request.didSuspendTaskNotification, with: request)
    }

    public func request(_ request: Request, didCancelTask task: URLSessionTask) {
        NotificationCenter.default.postNotification(named: Request.didCancelTaskNotification, with: request)
    }

    public func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) {
        NotificationCenter.default.postNotification(named: Request.didCompleteTaskNotification, with: request)
    }
}
複製代碼

Validation

收到請求響應後,咱們可能會須要先對響應進行校驗,來判斷本次響應是否有效,Alamofire在Request定義了validate方法能夠爲Request添加用來校驗有效性的閉包,在收到響應後,就會執行這些閉包來校驗本次有效性是否有效

class Request {
    // 保存使用validation來進行有效性校驗的閉包
    @Protected
    fileprivate var validators: [() -> Void] = []
    
    //添加validation,方法會建立一個無出入參數的閉包,用來執行validation校驗,而後把這個閉包暫存起來。
    @discardableResult
    public func validate(_ validation: @escaping Validation) -> Self 
}
複製代碼

而上面用到的Validation類型,就是在Validation.swift中定義的一個別名,是一個閉包,不一樣的Request子類有各自的Validation定義。而後在Validation.swift中,對Request進行了各類擴展,添加了默認的檢測有效性的方法:檢測響應碼與MIME類型等方式來進行判斷。

整個Validation.swift中分爲兩大部份內容:

  1. Request基類擴展,定義輔助類型,私有的響應碼、MIME類型校驗方法供第二部分的三個子類進行調用
  2. Request的三個子類擴展,實現校驗響應碼與MIME方法

1.Request基類擴展

大部份內容都是fileprivate級的,public級別的只有定義了一個Validation別名,供外部使用。fileprivate級別的內容主要是定義了默認的MIME類型,與默認容許的狀態碼,以及幾個供三個子類調用的校驗方法。

extension Request {
    
    //MARK: - 輔助類型定義
    // 只有這個別名是public的, 其他的都是fileprivate的
    // 校驗結果別名, 用來標識校驗結果正確與否
    public typealias ValidationResult = Result<Void, Error>
    
    // 錯誤緣由別名
    fileprivate typealias ErrorReason = AFError.ResponseValidationFailureReason

    // MIME類型結構體 "image/jpeg"
    fileprivate struct MIMEType {
        let type: String
        let subtype: String

        // 是不是通配類型的MIME
        var isWildcard: Bool { type == "*" && subtype == "*" }

        // 從string格式初始化, 失敗返回nil
        init?(_ string: String) {
            let components: [String] = {
                let stripped = string.trimmingCharacters(in: .whitespacesAndNewlines)
                let split = stripped[..<(stripped.range(of: ";")?.lowerBound ?? stripped.endIndex)]

                return split.components(separatedBy: "/")
            }()

            if let type = components.first, let subtype = components.last {
                self.type = type
                self.subtype = subtype
            } else {
                return nil
            }
        }

        // 校驗MIME
        func matches(_ mime: MIMEType) -> Bool {
            switch (type, subtype) {
            case (mime.type, mime.subtype), (mime.type, "*"), ("*", mime.subtype), ("*", "*"):
                return true
            default:
                return false
            }
        }
    }

    // 默認容許的響應狀態碼 2xx - 3xx
    fileprivate var acceptableStatusCodes: Range<Int> { 200..<300 }
    // 默認容許的ContentType, 會從請求頭中的Accept字段取值
    fileprivate var acceptableContentTypes: [String] {
        if let accept = request?.value(forHTTPHeaderField: "Accept") {
            return accept.components(separatedBy: ",")
        }

        return ["*/*"]
    }

    // 校驗響應的狀態碼是否與在傳入的狀態碼集合中
    fileprivate func validate<S: Sequence>(statusCode acceptableStatusCodes: S, response: HTTPURLResponse)
        -> ValidationResult
        where S.Iterator.Element == Int {
        // 若是支持的狀態碼中包含響應的狀態碼, 返回成功, 不然返回對應錯誤
        if acceptableStatusCodes.contains(response.statusCode) {
            return .success(())
        } else {
            let reason: ErrorReason = .unacceptableStatusCode(code: response.statusCode)
            return .failure(AFError.responseValidationFailed(reason: reason))
        }
    }

    // 校驗響應的contentType是否在傳入的contentType集合中
    // 入參有個Data, 若是data爲nil, 那就直接認爲contentType類型符合
    fileprivate func validate<S: Sequence>(contentType acceptableContentTypes: S, response: HTTPURLResponse, data: Data?)
        -> ValidationResult
        where S.Iterator.Element == String {
        guard let data = data, !data.isEmpty else { return .success(()) }

        return validate(contentType: acceptableContentTypes, response: response)
    }

    // 校驗響應的contentType是否在傳入的contentType集合中
    fileprivate func validate<S: Sequence>(contentType acceptableContentTypes: S, response: HTTPURLResponse)
        -> ValidationResult
        where S.Iterator.Element == String {
        // 先獲取響應的MIME類型
        guard
            let responseContentType = response.mimeType,
            let responseMIMEType = MIMEType(responseContentType)
        else {
            // 獲取響應的MIME類型失敗, 檢測下容許的ContentType是否是通配類型
            for contentType in acceptableContentTypes {
                if let mimeType = MIMEType(contentType), mimeType.isWildcard {
                    return .success(())
                }
            }
            
            // 未能獲取到響應的MIME, 且請求的ContentType不是通配的
            let error: AFError = {
                let reason: ErrorReason = .missingContentType(acceptableContentTypes: Array(acceptableContentTypes))
                return AFError.responseValidationFailed(reason: reason)
            }()

            return .failure(error)
        }

        // 獲取到MIME, 開始校驗
        for contentType in acceptableContentTypes {
            if let acceptableMIMEType = MIMEType(contentType), acceptableMIMEType.matches(responseMIMEType) {
                // 校驗成功
                return .success(())
            }
        }

        // 校驗失敗, 返回錯誤
        let error: AFError = {
            let reason: ErrorReason = .unacceptableContentType(acceptableContentTypes: Array(acceptableContentTypes),
                                                               responseContentType: responseContentType)

            return AFError.responseValidationFailed(reason: reason)
        }()

        return .failure(error)
    }
}
複製代碼

2.三個子類擴展

三個子類擴展行爲一致,都是先定義了Validation閉包別名的類型, 而後分別添加了三個校驗方法:

  1. 校驗指定狀態碼集合
  2. 校驗指定contentType集合
  3. 校驗默認的狀態碼與contentType集合

這三個校驗方法返回的都是Self類型,所以能夠串起來進行屢次校驗

// MARK: - DataRequest子類擴展

extension DataRequest {
    
    // 執行校驗的閉包
    public typealias Validation = (URLRequest?, HTTPURLResponse, Data?) -> ValidationResult

    // 指定狀態碼校驗
    @discardableResult
    public func validate<S: Sequence>(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int {
        // 調用定義中的校驗方法, 添加閉包
        validate { [unowned self] _, response, _ in
            self.validate(statusCode: acceptableStatusCodes, response: response)
        }
    }

    // 指定contentType校驗
    // 注意contentType是自動閉包
    // 把執行獲取contentType的時機推遲到了要執行校驗的時候調用
    // 這樣若是在請求完成前, 取消了請求, 就能夠不用獲取比較的contentType, 節省資源
    @discardableResult
    public func validate<S: Sequence>(contentType acceptableContentTypes: @escaping @autoclosure () -> S) -> Self where S.Iterator.Element == String {
        validate { [unowned self] _, response, data in
            self.validate(contentType: acceptableContentTypes(), response: response, data: data)
        }
    }

    // 使用默認的狀態碼與contentType
    @discardableResult
    public func validate() -> Self {
        // 這裏多餘了, 原本contentType參數就已是自動閉包了, 能夠直接丟進去用, 不須要再包一層閉包
        // validate(statusCode: acceptableStatusCodes).validate(contentType: self.acceptableContentTypes)
        let contentTypes: () -> [String] = { [unowned self] in
            self.acceptableContentTypes
        }
        // 鏈式調用, 先校驗響應碼, 再校驗contentType
        return validate(statusCode: acceptableStatusCodes).validate(contentType: contentTypes())
    }
}

//MARK: - DataStreamRequest
extension DataStreamRequest {
    /// A closure used to validate a request that takes a `URLRequest` and `HTTPURLResponse` and returns whether the
    /// request was valid.
    public typealias Validation = (_ request: URLRequest?, _ response: HTTPURLResponse) -> ValidationResult

    /// Validates that the response has a status code in the specified sequence.
    ///
    /// If validation fails, subsequent calls to response handlers will have an associated error.
    ///
    /// - Parameter statusCode: `Sequence` of acceptable response status codes.
    ///
    /// - Returns: The instance.
    @discardableResult
    public func validate<S: Sequence>(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int {
        validate { [unowned self] _, response in
            self.validate(statusCode: acceptableStatusCodes, response: response)
        }
    }

    /// Validates that the response has a content type in the specified sequence.
    ///
    /// If validation fails, subsequent calls to response handlers will have an associated error.
    ///
    /// - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes.
    ///
    /// - returns: The request.
    @discardableResult
    public func validate<S: Sequence>(contentType acceptableContentTypes: @escaping @autoclosure () -> S) -> Self where S.Iterator.Element == String {
        validate { [unowned self] _, response in
            self.validate(contentType: acceptableContentTypes(), response: response)
        }
    }

    /// Validates that the response has a status code in the default acceptable range of 200...299, and that the content
    /// type matches any specified in the Accept HTTP header field.
    ///
    /// If validation fails, subsequent calls to response handlers will have an associated error.
    ///
    /// - Returns: The instance.
    @discardableResult
    public func validate() -> Self {
        let contentTypes: () -> [String] = { [unowned self] in
            self.acceptableContentTypes
        }
        return validate(statusCode: acceptableStatusCodes).validate(contentType: contentTypes())
    }
}

// MARK: - DownloadRequest

extension DownloadRequest {
    /// A closure used to validate a request that takes a URL request, a URL response, a temporary URL and a
    /// destination URL, and returns whether the request was valid.
    public typealias Validation = (_ request: URLRequest?,
                                   _ response: HTTPURLResponse,
                                   _ fileURL: URL?)
        -> ValidationResult

    /// Validates that the response has a status code in the specified sequence.
    ///
    /// If validation fails, subsequent calls to response handlers will have an associated error.
    ///
    /// - Parameter statusCode: `Sequence` of acceptable response status codes.
    ///
    /// - Returns: The instance.
    @discardableResult
    public func validate<S: Sequence>(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int {
        validate { [unowned self] _, response, _ in
            self.validate(statusCode: acceptableStatusCodes, response: response)
        }
    }

    /// Validates that the response has a content type in the specified sequence.
    ///
    /// If validation fails, subsequent calls to response handlers will have an associated error.
    ///
    /// - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes.
    ///
    /// - returns: The request.
    @discardableResult
    public func validate<S: Sequence>(contentType acceptableContentTypes: @escaping @autoclosure () -> S) -> Self where S.Iterator.Element == String {
        validate { [unowned self] _, response, fileURL in
            guard let validFileURL = fileURL else {
                return .failure(AFError.responseValidationFailed(reason: .dataFileNil))
            }

            do {
                let data = try Data(contentsOf: validFileURL)
                return self.validate(contentType: acceptableContentTypes(), response: response, data: data)
            } catch {
                return .failure(AFError.responseValidationFailed(reason: .dataFileReadFailed(at: validFileURL)))
            }
        }
    }

    /// Validates that the response has a status code in the default acceptable range of 200...299, and that the content
    /// type matches any specified in the Accept HTTP header field.
    ///
    /// If validation fails, subsequent calls to response handlers will have an associated error.
    ///
    /// - returns: The request.
    @discardableResult
    public func validate() -> Self {
        let contentTypes = { [unowned self] in
            self.acceptableContentTypes
        }
        return validate(statusCode: acceptableStatusCodes).validate(contentType: contentTypes())
    }
}

複製代碼

Result+Alamofire

請求的結果,Alamofire使用了Result來進行封裝,成功對象爲泛型Success,錯誤對象爲AFError,所以Alamofire對Result進行了擴展,添加了一些輔助函數方便使用

// 請求結果類型別名, 請求結果使用Result來包裝, 錯誤類型爲AFError
public typealias AFResult<Success> = Result<Success, AFError>


// 這裏的擴展時internal級別的, module以外沒法訪問
extension Result {
    // 快速判斷成功
    var isSuccess: Bool {
        guard case .success = self else { return false }
        return true
    }
    // 快速判斷失敗
    var isFailure: Bool {
        !isSuccess
    }
    // 快速獲取成功時的值(失敗的時候返回nil)
    var success: Success? {
        guard case let .success(value) = self else { return nil }
        return value
    }
    // 快速獲取失敗時的錯誤(成功時返回nil)
    var failure: Failure? {
        guard case let .failure(error) = self else { return nil }
        return error
    }
    init(value: Success, error: Failure?) {
        if let error = error {
            self = .failure(error)
        } else {
            self = .success(value)
        }
    }
    // 把成功時的值類型變換成新類型(失敗時不處理)
    // 相似Result自帶的map函數, 區別是transform參數, 能夠拋出錯誤
    func tryMap<NewSuccess>(_ transform: (Success) throws -> NewSuccess) -> Result<NewSuccess, Error> {
        switch self {
        case let .success(value):
            // 成功時執行變換,do-catch捕捉異常並返回錯誤
            do {
                return try .success(transform(value))
            } catch {
                return .failure(error)
            }
        case let .failure(error):
            // 失敗時不作處理
            return .failure(error)
        }
    }
    // 把失敗時的錯誤類型變換成新類型(成功時不處理)
    // 相似Result自帶的mapError, 區別一樣是transform能夠拋出異常
    func tryMapError<NewFailure: Error>(_ transform: (Failure) throws -> NewFailure) -> Result<Success, Error> {
        switch self {
        case let .failure(error):
            //錯誤時執行變換,do-catch捕捉異常返回錯誤
            do {
                return try .failure(transform(error))
            } catch {
                return .failure(error)
            }
        case let .success(value):
            // 成功時不作處理
            return .success(value)
        }
    }
}
複製代碼

OperationQueue+Alamofire

只是簡單的擴展了OperationQueue,添加了快速初始化的方法

extension OperationQueue {
    convenience init(qualityOfService: QualityOfService = .default,//隊列優先級 maxConcurrentOperationCount: Int = OperationQueue.defaultMaxConcurrentOperationCount,// 最大併發op數 underlyingQueue: DispatchQueue? = nil,// 設置GCD隊列 name: String? = nil,// 標識符 startSuspended: Bool = false) {//是否初始化以後馬上掛起
        self.init()
        self.qualityOfService = qualityOfService
        self.maxConcurrentOperationCount = maxConcurrentOperationCount
        self.underlyingQueue = underlyingQueue
        self.name = name
        isSuspended = startSuspended
    }
}
複製代碼

StringEncoding+Alamofire

擴展String.Encoding,添加從字符串建立編碼類型的方法

extension String.Encoding {
    /// Creates an encoding from the IANA charset name.
    ///
    /// - Notes: These mappings match those [provided by CoreFoundation](https://opensource.apple.com/source/CF/CF-476.18/CFStringUtilities.c.auto.html)
    ///
    /// - Parameter name: IANA charset name.
    init?(ianaCharsetName name: String) {
        switch name.lowercased() {
        case "utf-8":
            self = .utf8
        case "iso-8859-1":
            self = .isoLatin1
        case "unicode-1-1", "iso-10646-ucs-2", "utf-16":
            self = .utf16
        case "utf-16be":
            self = .utf16BigEndian
        case "utf-16le":
            self = .utf16LittleEndian
        case "utf-32":
            self = .utf32
        case "utf-32be":
            self = .utf32BigEndian
        case "utf-32le":
            self = .utf32LittleEndian
        default:
            return nil
        }
    }
}
複製代碼

DispatchQueue+Alamofire

添加了快速異步延遲執行的方法,系統的after參數傳入的時間參數是DispatchTime類型,這裏添加的方法,時間參數是秒(s)

extension DispatchQueue {
    /// Execute the provided closure after a `TimeInterval`.
    ///
    /// - Parameters:
    /// - delay: `TimeInterval` to delay execution.
    /// - closure: Closure to execute.
    func after(_ delay: TimeInterval, execute closure: @escaping () -> Void) {
        asyncAfter(deadline: .now() + delay, execute: closure)
    }
}
複製代碼

URLSessionConfiguration+Alamofire

使用AlamofireExtended添加了默認的cfg

extension URLSessionConfiguration: AlamofireExtended {}
extension AlamofireExtension where ExtendedType: URLSessionConfiguration {
    // Alamofire默認的cfg, 跟URLSessionConfiguration.default同樣
    // 可是添加了默認請求頭: `Accept-Language`, `Accept-Encoding`, `User-Agent`
    // 詳情能夠點進下面這個.default中去看
    public static var `default`: URLSessionConfiguration {
        let configuration = URLSessionConfiguration.default
        configuration.headers = .default

        return configuration
    }
}
複製代碼

Combine

使用iOS 13出的Combine框架,對Request及其子類進行了擴展,使其支持Combine中的一些時間發佈與綁定。由於還沒去細看Combine框架,因此這部份內容等後續學習了Combine框架以後,再來學習。

純屬我的理解, 可能存在理解錯誤的地方, 若有錯誤, 歡迎評論指出~ 感謝~

相關文章
相關標籤/搜索