Previous navigation:

Alamofire source learning directory collection

Introduction to the

Alamofire adds many methods to request-related types by extension. These methods are related to the internal business of Alamofire as well as some functions of auxiliary tools. After a clear look, it is helpful to better understand the architectural design of Alamofire.

The relevant documents

AlamofireExtended.swift // The Alamofire extension wrapper is used when extending system classes to avoid method intrusion
Notifications.swift // The notification related utility class defines and extends the various notifications and related notification methods used in Alamofire, as well as an event monitor to send these notifications
Validation.swift // Extend Request and its subclasses to check the validity of the response
Combine.swift // Related extension of the Combine framework in Swift
OperationQueue+Alamofire.swift //OP queue extension, quick initialization use
StringEncoding+Alamofire.swift // Text encoding type conversion extension
Result+Alamofire.swift // Quickly process the internal type of Result and related decisions
URLSessionConfiguration+Alamofire.swift // Create the default configuration quickly
DispatchQueue+Alamofire.swift //GCD queue extension, quick delay execution closure use
Copy the code

AlamofireExtended

The Swift – based generic extension constraint provides the freedom to add methods to classes that need to be extended without excessive method contamination of the original type.

When we need to add a new method to an existing class, there are two methods available under Swift:

  1. Add global methods directly
  2. Extend the class to add methods

These two methods can be implemented as the class to add a new method, but 1 is a global, need to use through the form of method calls, 2, will have a problem: extension method, can directly effect on the type, can direct access to the method in the whole project, such as the us as a UIButton added the following three methods:

extension UIButton {
    func atap1(a) {}
    func atap2(a) {}
    func atap3(a){}}Copy the code

These methods can then be called anywhere the extension is accessible:Here’s the problem: These three methods appear directly in the method hints that button can call. Add in 100 extension methods, and these methods are strongly business related, so this extension method will cause all buttons to see these method hints when they want to call a method. On the one hand, it will cause confusion to the caller. On the other hand, there is the risk of the same name. This is called method pollution.

To solve the above problem, we can use the following idea to solve:

  • Instead of doing an extension method to a UIButton directly, you wrap the button around a struct
  • Then add a method to that struct
  • To call a method, we need to extend a method on the button to get the struct wrapped around the button, and then use that struct to call these methods:
struct ButtonWrapper {
    let btn: UIButton
    
    func atap1(a) {}
    func atap2(a) {}
    func atap3(a){}}extension UIButton {
    var btnWrapper: ButtonWrapper {
        return ButtonWrapper.init(btn: self)}}func test(a) {
    
    let btn: UIButton!
    
    btn.btnWrapper.atap1()
    
    
}
Copy the code

As you can see, the methods we want to add to the button will no longer appear in the button’s callable list. After adding a method that gets a Wrapper to the button’s method list, we can add as many methods as we want without contaminating the button. Good implementation of method isolation.

The above implementation has great limitations and can only add methods to UIButton. Due to the powerful Swift generics and extension constraints, we can write ButtonWrapper as a generic wrapper, which can wrap a generic object of any type. You then extend the wrapper and constrain the generic type by adding methods for the type you want.

public struct AlamofireExtension<ExtendedType> {
    // Wrap a generic type object that needs to be extended
    public private(set) var type: ExtendedType

    public init(_ type: ExtendedType) {
        self.type = type
    }
}
Copy the code

When you add an extension method to any type, you do so by extending AlamofireExtension and using the WHERE constraint

extension AlamofireExtension where ExtendedType= =UIButton {
    func atap1(a) {}
    func atap2(a) {}
    func atap3(a){}}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(a) {
    
    var btn: UIButton!
    btn.af.atap1()
    
    var lbl: UILabel!
    lbl.af.aText1
    
}
Copy the code

Each time you need to add a method to a new type, you need to extend the type. Add the AF computed property to return the AlamofireExtension package type. This behavior is highly similar across different types.


// Use protocol + protocol extensions to implement the ability to freely add packages to objects that need to be extended
public protocol AlamofireExtended {
    // The type of the object to be extended
    associatedtype ExtendedType

    // Static (class) wrapper
    static var af: AlamofireExtension<ExtendedType>.Type { get set }
    // Instance variable wrappers
    var af: AlamofireExtension<ExtendedType> { get set}}// Add default implementation for extension protocol
extension AlamofireExtended {
    // Static (class) wrapper, which wraps Type
    public static var af: AlamofireExtension<Self>.Type {
        get { AlamofireExtension<Self>.self }
        set{}}// The instance wrapper wraps the object itself
    public var af: AlamofireExtension<Self> {
        get { AlamofireExtension(self)}set{}}}Copy the code

In this case, the way we need to add to a type is to implement the AlamofireExtended protocol by adding an extension to that type to implement the method to create a wrapper, and then the extended wrapper is free to add various methods to that type.

extension AlamofireExtension where ExtendedType= =UIButton {
    func atap1(a) {}
    func atap2(a) {}
    func atap3(a){}}extension AlamofireExtension where ExtendedType= =UILabel {
    var aText1: String { return ""}}extension UIButton: AlamofireExtended {}
extension UILabel: AlamofireExtended {}

func test(a) {
    
    let btn: UIButton!
    btn.af.atap1()
    
    let lbl: UILabel!
    lbl.af.aText1
    
}
Copy the code

Within Alamofire, there are two main areas where AlamofireExtended is implemented:

  1. URLSessionConfiguration
  2. There are many types of certificate processing in ServerTrustEvaluation that implement this protocol

Finally blow Swift!! This design is quite clever, by using the call of ***.af first, you can also see at a glance that the method to be called is the extension method related to Alamofire.

Notifications

Many similar notifications are used in Alamofire, which are mainly sent out when the Request state changes. Therefore, the names of these notifications are defined in Alamofire, and the Request object is used to send and receive notifications by extending Notification and NotificationCenter. At the same time, an event listener is implemented to listen to the status of the request process and send the corresponding notification.

// Extend Request to add static constants to define the name of the notification
/ / use: 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: -

// Extend the notification object to quickly read and write the Request object from userInfo
// but instead of using AlamofireExtended, add it directly
extension Notification {
    // Obtain the Request object from userInfo
    public var request: Request? {
        userInfo?[String.requestKey] as? Request
    }

    // Create a notification object and insert the Request into userInfo to send it out
    init(name: Notification.Name.request: Request) {
        self.init(name: name, object: nil, userInfo: [String.requestKey: request])
    }
    
    // Use simulation
    func test(a) {
        var req: Request!
        let noti = Notification.init(name: Request.didResumeNotification, request: req)
        let getReq = noti.request
    }
}

//***************** The following is a personal addition *****************//
// Personally, it is better to use wrappers for method isolation for this type of extension method with business relevance
extension Notification: AlamofireExtended {}
extension AlamofireExtension where ExtendedType= =Notification {
    public var request: Request? {
        type.userInfo?[String.requestKey] as? Request
    }

    // Create a notification object and insert the Request into userInfo to send it out
    static func createNotificationWith(name: Notification.Name.request: Request) -> Notification {
        Notification.init(name: name, object: nil, userInfo: [String.requestKey: request])
    }
    
    // Use simulation
    func test(a) {
        var req: Request!
        let noti = Notification.af.createNotificationWith(name: Request.didResumeNotification, request: req)
        let getReq = noti.af.request
    }
}
//***************** The above is a personal addition *****************//

extension NotificationCenter {
    // Send notifications quickly
    func postNotification(named name: Notification.Name.with request: Request) {
        let notification = Notification(name: name, request: request)
        post(notification)
    }
}

extension String {
    // Save the Request key in the notification userInfo
    fileprivate static let requestKey = "org.alamofire.notification.key.request"
}

// The event listener is used to send the corresponding notifications in each phase
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)
    }
}
Copy the code

Validation

After receiving the Request and response, we may need to verify the response to determine whether the response is valid. Alamofire defines the validate method in Request to add closures for verifying the validity of the Request. After receiving the response, these closures will be executed to verify the validity of the response

class Request {
    // Save the closure that uses validation for validation
    @Protected
    fileprivate var validators: [() -> Void] = []
    
    // Add validation. The method creates a closure with no input arguments to perform validation, and then stores the closure temporarily.
    @discardableResult
    public func validate(_ validation: @escaping Validation) -> Self 
}
Copy the code

Validation is an alias defined in Validation.swift, and is a closure. Different Request subclasses have their own Validation definitions. In Validation.swift, we extend the Request by adding the default Validation methods: check the response code and MIME type.

There are two main parts to validation. swift:

  1. Request extends the base class to define auxiliary types, private response codes, and MIME type validators that are called by the three subclasses of Part 2
  2. Request’s three subclasses extend the implementation of the verification response code and MIME methods

1.Request base class extension

Most of the content is at the Fileprivate level; the public level only defines a Validation alias for external use. The fileprivate level defines a default MIME type, a default allowed status code, and several validation methods for the three subclasses to call.

extension Request {
    
    //MARK: - Auxiliary type definition
    // Only this alias is public, the rest are fileprivate
    // The alias of the verification result is used to identify whether the verification result is correct
    public typealias ValidationResult = Result<Void.Error>
    
    // Error cause alias
    fileprivate typealias ErrorReason = AFError.ResponseValidationFailureReason

    // MIME type structure "image/jpeg"
    fileprivate struct MIMEType {
        let type: String
        let subtype: String

        // Is it a wildcard MIME
        var isWildcard: Bool { type = = "*" && subtype = = "*" }

        // Initialize from string format, return nil on failure
        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}}/ / check MIME
        func matches(_ mime: MIMEType) -> Bool {
            switch (type, subtype) {
            case (mime.type, mime.subtype), (mime.type, "*"), ("*", mime.subtype), ("*"."*") :return true
            default:
                return false}}}// The default allowed response status code is 2xx-3xx
    fileprivate var acceptableStatusCodes: Range<Int> { 200..<300 }
    // The default allowed ContentType is taken from the Accept field in the request header
    fileprivate var acceptableContentTypes: [String] {
        if let accept = request?.value(forHTTPHeaderField: "Accept") {
            return accept.components(separatedBy: ",")}return ["* / *"]}// Check whether the status code of the response is in the set of status codes passed in
    fileprivate func validate<S: Sequence> (statusCode acceptableStatusCodes: S.response: HTTPURLResponse)
        -> ValidationResult
        where S.Iterator.Element = = Int {
        // If the supported status code contains the response status code, an error is returned
        if acceptableStatusCodes.contains(response.statusCode) {
            return .success(())
        } else {
            let reason: ErrorReason = .unacceptableStatusCode(code: response.statusCode)
            return .failure(AFError.responseValidationFailed(reason: reason))
        }
    }

    // Verify that the contentType of the response is in the set of contentTypes passed in
    // The input parameter has a Data. If the Data is nil, then the contentType type is assumed to match
    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)
    }

    // Verify that the contentType of the response is in the set of contentTypes passed in
    fileprivate func validate<S: Sequence> (contentType acceptableContentTypes: S.response: HTTPURLResponse)
        -> ValidationResult
        where S.Iterator.Element = = String {
        // Get the MIME type of the response first
        guard
            let responseContentType = response.mimeType,
            let responseMIMEType = MIMEType(responseContentType)
        else {
            // Failed to get the MIME type of the response. Check if the allowed ContentType is a wildcard type
            for contentType in acceptableContentTypes {
                if let mimeType = MIMEType(contentType), mimeType.isWildcard {
                    return .success(())
                }
            }
            
            // The MIME of the response could not be obtained, and the ContentType of the request is not wildcard
            let error: AFError = {
                let reason: ErrorReason = .missingContentType(acceptableContentTypes: Array(acceptableContentTypes))
                return AFError.responseValidationFailed(reason: reason)
            }()

            return .failure(error)
        }

        // Get MIME and start validation
        for contentType in acceptableContentTypes {
            if let acceptableMIMEType = MIMEType(contentType), acceptableMIMEType.matches(responseMIMEType) {
                // The verification succeeded
                return .success(())
            }
        }

        // Check failed and an error is returned
        let error: AFError = {
            let reason: ErrorReason = .unacceptableContentType(acceptableContentTypes: Array(acceptableContentTypes),
                                                               responseContentType: responseContentType)

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

        return .failure(error)
    }
}
Copy the code

2. Three subclass extensions

The three subclass extensions behave in the same way by defining the Validation closure alias and then adding three Validation methods:

  1. Verifies the specified set of status codes
  2. Validates the specified contentType collection
  3. Validates the default status code against the contentType collection

All three check methods return type Self, so you can string them together for multiple checks



// MARK: - DataRequest subclass extension

extension DataRequest {
    
    // The closure to perform the verification
    public typealias Validation = (URLRequest? .HTTPURLResponse.Data?). ->ValidationResult

    // Specify status code verification
    @discardableResult
    public func validate<S: Sequence> (statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element = = Int {
        // Add the closure by calling the validation method in the definition
        validate { [unowned self] _, response, _ in
            self.validate(statusCode: acceptableStatusCodes, response: response)
        }
    }

    // Specify contentType verification
    // Note that contentType is an automatic closure
    // Delay the execution of retrieving the contentType until it is time to perform validation
    // If you cancel the request before the request is complete, you don't need to retrieve the comparison contentType, saving resources
    @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)
        }
    }

    // Use the default status code and contentType
    @discardableResult
    public func validate(a) -> Self {
        // The contentType parameter is already an automatic closure, so you can throw it in. You don't need to wrap another closure
        // validate(statusCode: acceptableStatusCodes).validate(contentType: self.acceptableContentTypes)
        let contentTypes: () -> [String] ={[unowned self] in
            self.acceptableContentTypes
        }
        // String call, verify the response code first, then verify the 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(a) -> 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(a) -> Self {
        let contentTypes ={[unowned self] in
            self.acceptableContentTypes
        }
        return validate(statusCode: acceptableStatusCodes).validate(contentType: contentTypes())
    }
}

Copy the code

Result+Alamofire

As for the Result of the request, Alamofire uses Result for encapsulation. The Success object is generic Success and the error object is AFError. Therefore, Alamofire extends the Result and adds some auxiliary functions for convenience

// Request Result type alias, request Result wrapped in Result, error type AFError
public typealias AFResult<Success> = Result<Success.AFError>


// The extension here is internal and cannot be accessed outside the Module
extension Result {
    // Judge success quickly
    var isSuccess: Bool {
        guard case .success = self else { return false }
        return true
    }
    // Quick judgment failed
    var isFailure: Bool {
        !isSuccess
    }
    // Quickly retrieve the value on success (return nil on failure)
    var success: Success? {
        guard case let .success(value) = self else { return nil }
        return value
    }
    // Quickly retrieve errors on failure (return nil on success)
    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)
        }
    }
    // Convert the value type on success to the new type (not handled on failure)
    // This is similar to the map function that comes with Result, except that the transform parameter can throw an error
    func tryMap<NewSuccess> (_ transform: (Success) throws -> NewSuccess) -> Result<NewSuccess.Error> {
        switch self {
        case let .success(value):
            // Perform the transformation on success. Do-catch catches the exception and returns an error
            do {
                return try .success(transform(value))
            } catch {
                return .failure(error)
            }
        case let .failure(error):
            // Do not handle failure
            return .failure(error)
        }
    }
    // Change the error type on failure to the new type (not handled on success)
    // This is similar to Result's mapError, except that transform can also throw an exception
    func tryMapError<NewFailure: Error> (_ transform: (Failure) throws -> NewFailure) -> Result<Success.Error> {
        switch self {
        case let .failure(error):
            // When an error occurs, the transformation is performed. Do -catch the exception and return the error
            do {
                return try .failure(transform(error))
            } catch {
                return .failure(error)
            }
        case let .success(value):
            // No processing on success
            return .success(value)
        }
    }
}
Copy the code

OperationQueue+Alamofire

Simply extend OperationQueue to add a fast initialization method

extension OperationQueue {
    convenience init(qualityOfService: QualityOfService = .default,// Queue priority
                     maxConcurrentOperationCount: Int = OperationQueue.defaultMaxConcurrentOperationCount,// Maximum number of concurrent op
                     underlyingQueue: DispatchQueue? = nil.// Set the GCD queue
                     name: String? = nil./ / identifier
                     startSuspended: Bool = false) {// Whether to suspend immediately after initialization
        self.init(a)self.qualityOfService = qualityOfService
        self.maxConcurrentOperationCount = maxConcurrentOperationCount
        self.underlyingQueue = underlyingQueue
        self.name = name
        isSuspended = startSuspended
    }
}
Copy the code

StringEncoding+Alamofire

Extend string. Encoding to add a method for creating Encoding types from strings

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}}}Copy the code

DispatchQueue+Alamofire

– Added a method for fast asynchronous delay execution. The system’s after parameter is passed with a time parameter of DispatchTime type. Here added a method with a time parameter of seconds (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)
    }
}
Copy the code

URLSessionConfiguration+Alamofire

A default CFG was added with AlamofireExtended

extension URLSessionConfiguration: AlamofireExtended {}
extension AlamofireExtension where ExtendedType: URLSessionConfiguration {
    / / Alamofire default CFG, with URLSessionConfiguration. Default
    // But add default request headers: 'accept-language', 'accept-encoding', 'user-agent'
    // For details, go to.default
    public static var `default`: URLSessionConfiguration {
        let configuration = URLSessionConfiguration.default
        configuration.headers = .default

        return configuration
    }
}
Copy the code

Combine

Request and its subclasses are extended by using the Combine framework of iOS 13 to support some time publishing and binding in Combine. Since we haven’t looked closely at the Combine framework, we will learn this part later after we learn the Combine framework.

Purely personal understanding, there may be misunderstanding, if there is a mistake, please comment to point out ~ thanks ~