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框架以后,再来学习。

纯属我的理解, 可能存在理解错误的地方, 若有错误, 欢迎评论指出~ 感谢~

相关文章
相关标签/搜索