Alamofire源碼解讀

Alamofire源碼解讀

AFNetworking的作者M(jìn)att Thompson 提出了一個(gè)新的類似AFNetworking的網(wǎng)絡(luò)基礎(chǔ)庫(kù)筋现,并且專門使用最新的Swift語(yǔ)言寫的珠移,名為 Alamofire.

對(duì)于使用OC的開(kāi)發(fā)者來(lái)說(shuō)一定十分熟悉AFNetworking這個(gè)框架韩脏,因?yàn)楝F(xiàn)在我們的app只要是有關(guān)于網(wǎng)絡(luò)訪問(wèn)的部分大部分都會(huì)通過(guò)這個(gè)框架來(lái)進(jìn)行網(wǎng)絡(luò)的訪問(wèn)。

而Alamofire 是 Swift 語(yǔ)言的 HTTP 網(wǎng)絡(luò)開(kāi)發(fā)工具包跨跨,功能強(qiáng)大蔑舞,支持各種 HTTP Method入偷、JSON、文件上傳眉枕、文件下載和多種認(rèn)證方法恶复。

所以當(dāng)我們采用Swift進(jìn)行開(kāi)發(fā)的時(shí)候,我們就有了更多的選擇,是采用OC的AFNetworking也好,還是采用Swift的Alamofire也行...

相信作為開(kāi)發(fā)者,大家都在其他地方或多或少的讀過(guò)關(guān)于AFNetworking的解析,有的甚至也自己做過(guò)AFNetworking的解析,
那么為什么還要再做Alamofire的解析呢?
原因有以下幾點(diǎn),

  1. 新的開(kāi)發(fā)語(yǔ)言,新的開(kāi)發(fā)思路
  2. 學(xué)習(xí)Swift規(guī)范開(kāi)發(fā)的一個(gè)很好的教程
  3. 網(wǎng)絡(luò)流程的學(xué)習(xí),請(qǐng)求怜森、響應(yīng)、解析谤牡、等等...
  4. AFAFNetworking和Alamofire之間的對(duì)比

OK 進(jìn)入正題.
首先作為一個(gè)新的網(wǎng)絡(luò)庫(kù),Alamofire同時(shí)作為AFnetworking的Swift版本,甚至是作為同一個(gè)作者的開(kāi)源庫(kù),無(wú)論從哪個(gè)角度來(lái)說(shuō),Alamofire都有很多和AFnetworking相似的地方...
如何對(duì)比,我們首先把Alamofire加上項(xiàng)目中看看
網(wǎng)上很容易找到方法,直接加入項(xiàng)目,或者是采用CocoaPods進(jìn)行安裝.
這里我也簡(jiǎn)單的展示一下

第一種方式
我們下載這個(gè)庫(kù)文件
https://github.com/Alamofire/Alamofire
我們創(chuàng)建一個(gè)項(xiàng)目并把a(bǔ)lamofire拖到整個(gè)項(xiàng)目根目錄中

這里寫圖片描述

打開(kāi)工程后選擇Alamofire.xcodeproj 拖入工程中
選擇自己的工程.xcodeproj->Build Phases->Link Binary With Libraries添加Alamofire.framework
這里寫圖片描述

之后可能需要在info.plist中添加(如果你的網(wǎng)路請(qǐng)求地址是http的話)
這里寫圖片描述

在需要的網(wǎng)絡(luò)請(qǐng)求的地方類似這樣使用即可


這里寫圖片描述

這樣手動(dòng)導(dǎo)入框架就已經(jīng)完成了
當(dāng)然作為開(kāi)發(fā)者,大部分都會(huì)使用CocoaPods的方式,這里我就不啰嗦了,大家自行百度使用CocoaPods的方式即可

OK,我們這里先不看使用的方式,我們開(kāi)始解讀源碼,我們打開(kāi)Alamofire我們可以看到所有的文件


這里寫圖片描述

如果使用的是CocoaPods的話也可以看到相同的


這里寫圖片描述

總共有17個(gè)文件
  • Alamofire-->基本接口的封裝
  • AFError-->錯(cuò)誤的封裝
  • Notifications-->通知的封裝
  • ParameterEncoding-->參數(shù)編碼的封裝
  • Request-->請(qǐng)求的封裝 (核心)
  • Response-->服務(wù)器返回?cái)?shù)據(jù)的封裝
  • Result-->請(qǐng)求結(jié)果的封裝
  • SessionDelegate-->會(huì)話代理的封裝
  • SessionManager-->會(huì)話管理的封裝 (核心)
  • TaskDelegate-->任務(wù)代理的封裝
  • DispatchQueue+Alamofire-->GCD的封裝
  • MultipartFormData-->多表單數(shù)據(jù)的封裝
  • NetworkReachabilityManager-->網(wǎng)絡(luò)狀態(tài)管理的封裝
  • ResponseSerialization-->響應(yīng)序列化管理的封裝
  • ServerTrustPolicy-->安全策略的封裝
  • Timeline-->時(shí)間軸的封裝
  • Validation-->服務(wù)器響應(yīng)的驗(yàn)證

我會(huì)按照網(wǎng)絡(luò)請(qǐng)求的整個(gè)過(guò)程來(lái)逐一解析

OK,作為一個(gè)網(wǎng)絡(luò)庫(kù),第一件我們會(huì)干的是什么事情呢?我會(huì)創(chuàng)造一個(gè)請(qǐng)求,
這里面包括,請(qǐng)求的地址,參數(shù),策略等等,然后由一個(gè)會(huì)話管理發(fā)起請(qǐng)求.并接受相應(yīng),
所以我們就先看這個(gè)ParameterEncoding
為什么呢?我們都知道,我們發(fā)起一個(gè)請(qǐng)求,會(huì)有這樣的一些設(shè)置,設(shè)置請(qǐng)求方式:get\post\put\delete...然后我們會(huì)設(shè)置請(qǐng)求協(xié)議:http\ftp...,接著設(shè)置域名,設(shè)置主機(jī)地址,路徑,端口號(hào),參數(shù)....
這樣才能夠組成一次完整的請(qǐng)求.否則我們連請(qǐng)求地址都不對(duì),還談什么請(qǐng)求呢?

ParameterEncoding部分

/// HTTP method definitions.
///
/// See https://tools.ietf.org/html/rfc7231#section-4.3
public enum HTTPMethod: String {
    case options = "OPTIONS"
    case get     = "GET"
    case head    = "HEAD"
    case post    = "POST"
    case put     = "PUT"
    case patch   = "PATCH"
    case delete  = "DELETE"
    case trace   = "TRACE"
    case connect = "CONNECT"
}

我們?cè)赑arameterEncoding中可以看到,這是一個(gè)http請(qǐng)求類型的枚舉.
在Swift中,枚舉跟OC有了很大的區(qū)別,使用的是case的方式.

public protocol ParameterEncoding {
    /// Creates a URL request by encoding parameters and applying them onto an existing request.
    ///
    /// - parameter urlRequest: The request to have parameters applied.
    /// - parameter parameters: The parameters to apply.
    ///
    /// - throws: An `AFError.parameterEncodingFailed` error if encoding fails.
    ///
    /// - returns: The encoded request.
    func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest
}

關(guān)于ParameterEncoding協(xié)議
這個(gè)協(xié)議中只有一個(gè)函數(shù)副硅,該函數(shù)需要兩個(gè)參數(shù):

urlRequest 該參數(shù)需要實(shí)現(xiàn)URLRequestConvertible協(xié)議,實(shí)現(xiàn)URLRequestConvertible協(xié)議的對(duì)象能夠轉(zhuǎn)換成URLRequest
parameters 參數(shù)拓哟,其類型為Parameters想许,也就是字典:public typealias Parameters = [String: Any]

該函數(shù)返回值類型為URLRequest。通過(guò)觀察這個(gè)函數(shù)断序,我們就明白了這個(gè)函數(shù)的目的就是把參數(shù)綁定到urlRequest之中流纹,至于返回的urlRequest是不是之前的urlRequest,這個(gè)不一定违诗,另一個(gè)比較重要的是該函數(shù)會(huì)拋出異常漱凝,因此在本篇后邊的解讀中會(huì)說(shuō)明該異常的來(lái)源。

我們已經(jīng)知道了URLEncoding就是和URL相關(guān)的編碼诸迟。當(dāng)把參數(shù)編碼到httpBody中這種情況是不受限制的茸炒,而直接編碼到URL中就會(huì)受限制,只有當(dāng)HTTPMethod為GET, HEAD and DELETE時(shí)才直接編碼到URL中阵苇。

public enum Destination {
        case methodDependent, queryString, httpBody
    }
methodDependent 根據(jù)HTTPMethod自動(dòng)判斷采取哪種編碼方式
queryString 拼接到URL中
httpBody 拼接到httpBody中
 // MARK: Properties

    /// Returns a default `URLEncoding` instance.
    public static var `default`: URLEncoding { return URLEncoding() }

    /// Returns a `URLEncoding` instance with a `.methodDependent` destination.
    public static var methodDependent: URLEncoding { return URLEncoding() }

    /// Returns a `URLEncoding` instance with a `.queryString` destination.
    public static var queryString: URLEncoding { return URLEncoding(destination: .queryString) }

    /// Returns a `URLEncoding` instance with an `.httpBody` destination.
    public static var httpBody: URLEncoding { return URLEncoding(destination: .httpBody) }

    /// The destination defining where the encoded query string is to be applied to the URL request.
    public let destination: Destination

    // MARK: Initialization

    /// Creates a `URLEncoding` instance using the specified destination.
    ///
    /// - parameter destination: The destination defining where the encoded query string is to be applied.
    ///
    /// - returns: The new `URLEncoding` instance.
    public init(destination: Destination = .methodDependent) {
        self.destination = destination

從這個(gè)地方我們可以看出Alamofire的URLEncoding提供了默認(rèn)的初始化選擇的Destination是methodDependent壁公,除了default這個(gè)單利外,又增加了其他的三個(gè)

這里采用了類屬性類創(chuàng)建單例

public static var `default`: URLEncoding { return URLEncoding() }

接下來(lái)是encode的方法

public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
        var urlRequest = try urlRequest.asURLRequest()//獲取UrlRequest

        guard let parameters = parameters else { return urlRequest }//如果參數(shù)為nil就直接返回urlRequest

        if let method = HTTPMethod(rawValue: urlRequest.httpMethod ?? "GET"), encodesParametersInURL(with: method) {// 參數(shù)編碼到url
            guard let url = urlRequest.url else {
                throw AFError.parameterEncodingFailed(reason: .missingURL)
            }
        //分解url
            if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), !parameters.isEmpty {
            //把原有的url中的query百分比編碼后在拼接上編碼后的參數(shù)
                let percentEncodedQuery = (urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters)
                urlComponents.percentEncodedQuery = percentEncodedQuery
                urlRequest.url = urlComponents.url
            }
        } else {// 參數(shù)編碼到httpBody
            if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {// 設(shè)置Content-Type
                urlRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
            }

            urlRequest.httpBody = query(parameters).data(using: .utf8, allowLossyConversion: false)
        }

        return urlRequest
    }

所以以上是url的組成,只有完整組成了url并且定義了請(qǐng)求方式,請(qǐng)求類型....才能夠正確的獲取到請(qǐng)求和響應(yīng).

上面的是參數(shù)的組成,但是在這之前參數(shù)是如何對(duì)應(yīng),轉(zhuǎn)碼并且轉(zhuǎn)化成字符串的呢?
我們可以看到上面有用到一個(gè)query的方法,我們學(xué)習(xí)一下
private func query(_ parameters: [String: Any]) -> String {
var components: [(String, String)] = []

    for key in parameters.keys.sorted(by: <) {
        let value = parameters[key]!
        components += queryComponents(fromKey: key, value: value)
    }

    return components.map { "\($0)=\($1)" }.joined(separator: "&")
}

看到了我們很熟悉的拼接字符串
上邊函數(shù)的整體思路是:

寫一個(gè)數(shù)組绅项,這個(gè)數(shù)組中存放的是元組數(shù)據(jù)紊册,元組中存放的是key和字符串類型的value
遍歷參數(shù),對(duì)參數(shù)做進(jìn)一步的處理快耿,然后拼接到數(shù)組中
進(jìn)一步處理數(shù)組內(nèi)部的元組數(shù)據(jù)囊陡,把元組內(nèi)部的數(shù)據(jù)用=號(hào)拼接,然后用符號(hào)&把數(shù)組拼接成字符串

這里面用到了

public func queryComponents(fromKey key: String, value: Any) -> [(String, String)] {
        var components: [(String, String)] = []

        if let dictionary = value as? [String: Any] {
            for (nestedKey, value) in dictionary {
                components += queryComponents(fromKey: "\(key)[\(nestedKey)]", value: value)
            }
        } else if let array = value as? [Any] {
            for value in array {
                components += queryComponents(fromKey: "\(key)[]", value: value)
            }
        } else if let value = value as? NSNumber {
            if value.isBool {
                components.append((escape(key), escape((value.boolValue ? "1" : "0"))))
            } else {
                components.append((escape(key), escape("\(value)")))
            }
        } else if let bool = value as? Bool {
            components.append((escape(key), escape((bool ? "1" : "0"))))
        } else {
            components.append((escape(key), escape("\(value)")))
        }

        return components
    }

我們可以看到value是Any的類型,所以在這個(gè)方法里面是做類型的轉(zhuǎn)換同時(shí)做轉(zhuǎn)碼,我們?cè)倏纯崔D(zhuǎn)碼方法

public func escape(_ string: String) -> String {
        let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4
        let subDelimitersToEncode = "!$&'()*+,;="

        var allowedCharacterSet = CharacterSet.urlQueryAllowed
        allowedCharacterSet.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")

        var escaped = ""

        //==========================================================================================================
        //
        //  Batching is required for escaping due to an internal bug in iOS 8.1 and 8.2. Encoding more than a few
        //  hundred Chinese characters causes various malloc error crashes. To avoid this issue until iOS 8 is no
        //  longer supported, batching MUST be used for encoding. This introduces roughly a 20% overhead. For more
        //  info, please refer to:
        //
        //      - https://github.com/Alamofire/Alamofire/issues/206
        //
        //==========================================================================================================

        if #available(iOS 8.3, *) {
            escaped = string.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? string
        } else {
            let batchSize = 50
            var index = string.startIndex

            while index != string.endIndex {
                let startIndex = index
                let endIndex = string.index(index, offsetBy: batchSize, limitedBy: string.endIndex) ?? string.endIndex
                let range = startIndex..<endIndex

                let substring = string.substring(with: range)

                escaped += substring.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? substring

                index = endIndex
            }
        }

        return escaped
    }

不同于網(wǎng)上流行的轉(zhuǎn)碼方式,但是大致上的思路仍然是一樣的,而且我可以跟你說(shuō),這個(gè)方法很優(yōu)秀,網(wǎng)上流傳的轉(zhuǎn)碼的方法有的時(shí)候仍然有局限性,但是這個(gè)不會(huì),最起碼我至今沒(méi)有發(fā)現(xiàn),所以無(wú)論是我用不用Alamofire,我都會(huì)把這個(gè)轉(zhuǎn)碼的方式給單獨(dú)拿出來(lái),在我需要的時(shí)候使用,屢試不爽...

到這里掀亥,URLEncoding的全部?jī)?nèi)容就分析完畢了撞反,我們把不同的功能劃分成不同的函數(shù),這種做法最大的好處就是我們可以使用單獨(dú)的函數(shù)做獨(dú)立的事情搪花。我完全可以使用escape這個(gè)函數(shù)轉(zhuǎn)義任何字符串遏片。

繼續(xù)向下看則是JSONEncoding
JSONEncoding的主要作用是把參數(shù)以JSON的形式編碼到request之中,當(dāng)然是通過(guò)request的httpBody進(jìn)行賦值的撮竿。JSONEncoding提供了兩種處理函數(shù)吮便,一種是對(duì)普通的字典參數(shù)進(jìn)行編碼,另一種是對(duì)JSONObject進(jìn)行編碼倚聚,處理這兩種情況的函數(shù)基本上是相同的

public struct JSONEncoding: ParameterEncoding {

    // MARK: Properties

    /// Returns a `JSONEncoding` instance with default writing options.
    public static var `default`: JSONEncoding { return JSONEncoding() }

    /// Returns a `JSONEncoding` instance with `.prettyPrinted` writing options.
    public static var prettyPrinted: JSONEncoding { return JSONEncoding(options: .prettyPrinted) }

    /// The options for writing the parameters as JSON data.
    public let options: JSONSerialization.WritingOptions

    // MARK: Initialization

    /// Creates a `JSONEncoding` instance using the specified options.
    ///
    /// - parameter options: The options for writing the parameters as JSON data.
    ///
    /// - returns: The new `JSONEncoding` instance.
    public init(options: JSONSerialization.WritingOptions = []) {
        self.options = options
    }

這里邊值得注意的是JSONSerialization.WritingOptions,也就是JSON序列化的寫入方式线衫。WritingOptions是一個(gè)結(jié)構(gòu)體,系統(tǒng)提供了一個(gè)選項(xiàng):prettyPrinted,意思是更好的打印效果

public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
        var urlRequest = try urlRequest.asURLRequest()

        guard let parameters = parameters else { return urlRequest }

        do {
            let data = try JSONSerialization.data(withJSONObject: parameters, options: options)

            if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
                urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
            }

            urlRequest.httpBody = data
        } catch {
            throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error))
        }

        return urlRequest
    }

public func encode(_ urlRequest: URLRequestConvertible, withJSONObject jsonObject: Any? = nil) throws -> URLRequest {
        var urlRequest = try urlRequest.asURLRequest()

        guard let jsonObject = jsonObject else { return urlRequest }

        do {
            let data = try JSONSerialization.data(withJSONObject: jsonObject, options: options)

            if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
                urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
            }

            urlRequest.httpBody = data
        } catch {
            throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error))
        }

        return urlRequest
    }

至于encode方法,第一個(gè)函數(shù)實(shí)現(xiàn)了ParameterEncoding協(xié)議惑折,第二個(gè)參數(shù)作為擴(kuò)展授账,函數(shù)中最核心的內(nèi)容是把參數(shù)變成Data類型枯跑,然后給httpBody賦值,需要注意的是異常處理白热。

至于接下來(lái)的PropertyListEncoding,基本上和JSONEncoding差不多是一樣的,這里就不介紹了

Request部分
有了參數(shù)和地址,那當(dāng)然的就是發(fā)起請(qǐng)求,自然而然的就到了request部分

enum RequestTask {
        case data(TaskConvertible?, URLSessionTask?)
        case download(TaskConvertible?, URLSessionTask?)
        case upload(TaskConvertible?, URLSessionTask?)
        case stream(TaskConvertible?, URLSessionTask?)
    }

這是一個(gè)task的枚舉,分別表示各種請(qǐng)求的方式,我們可以熟悉的看到系統(tǒng)的類URLSessionTask,所以說(shuō)Alamofire也是基于URLSession來(lái)寫的網(wǎng)絡(luò)框架

/// The underlying task.
    open var task: URLSessionTask? { return delegate.task }

    /// The session belonging to the underlying task.
    open let session: URLSession

    /// The request sent or to be sent to the server.
    open var request: URLRequest? { return task?.originalRequest }

    /// The response received from the server, if any.
    open var response: HTTPURLResponse? { return task?.response as? HTTPURLResponse }

    /// The number of times the request has been retried.
    open internal(set) var retryCount: UInt = 0

    let originalTask: TaskConvertible?

    var startTime: CFAbsoluteTime?
    var endTime: CFAbsoluteTime?

    var validations: [() -> Void] = []

    private var taskDelegate: TaskDelegate
    private var taskDelegateLock = NSLock()

    // MARK: Lifecycle

    init(session: URLSession, requestTask: RequestTask, error: Error? = nil) {
        self.session = session

        switch requestTask {
        case .data(let originalTask, let task):
            taskDelegate = DataTaskDelegate(task: task)
            self.originalTask = originalTask
        case .download(let originalTask, let task):
            taskDelegate = DownloadTaskDelegate(task: task)
            self.originalTask = originalTask
        case .upload(let originalTask, let task):
            taskDelegate = UploadTaskDelegate(task: task)
            self.originalTask = originalTask
        case .stream(let originalTask, let task):
            taskDelegate = TaskDelegate(task: task)
            self.originalTask = originalTask
        }

        delegate.error = error
        delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent() }
    }

這里是request的初始化和一些設(shè)置的默認(rèn)屬性...
這部分是auth

// MARK: Authentication

    /// Associates an HTTP Basic credential with the request.
    ///
    /// - parameter user:        The user.
    /// - parameter password:    The password.
    /// - parameter persistence: The URL credential persistence. `.ForSession` by default.
    ///
    /// - returns: The request.
    @discardableResult
    open func authenticate(
        user: String,
        password: String,
        persistence: URLCredential.Persistence = .forSession)
        -> Self
    {
        let credential = URLCredential(user: user, password: password, persistence: persistence)
        return authenticate(usingCredential: credential)
    }

    /// Associates a specified credential with the request.
    ///
    /// - parameter credential: The credential.
    ///
    /// - returns: The request.
    @discardableResult
    open func authenticate(usingCredential credential: URLCredential) -> Self {
        delegate.credential = credential
        return self
    }

    /// Returns a base64 encoded basic authentication credential as an authorization header tuple.
    ///
    /// - parameter user:     The user.
    /// - parameter password: The password.
    ///
    /// - returns: A tuple with Authorization header and credential value if encoding succeeds, `nil` otherwise.
    open static func authorizationHeader(user: String, password: String) -> (key: String, value: String)? {
        guard let data = "\(user):\(password)".data(using: .utf8) else { return nil }

        let credential = data.base64EncodedString(options: [])

        return (key: "Authorization", value: "Basic \(credential)")
    }

接下來(lái)并沒(méi)有什么特殊的方式
類似于NSURLSession 分別有對(duì)task的恢復(fù),暫停和取消

/// Resumes the request.
    open func resume() {
        guard let task = task else { delegate.queue.isSuspended = false ; return }

        if startTime == nil { startTime = CFAbsoluteTimeGetCurrent() }

        task.resume()

        NotificationCenter.default.post(
            name: Notification.Name.Task.DidResume,
            object: self,
            userInfo: [Notification.Key.Task: task]
        )
    }

    /// Suspends the request.
    open func suspend() {
        guard let task = task else { return }

        task.suspend()

        NotificationCenter.default.post(
            name: Notification.Name.Task.DidSuspend,
            object: self,
            userInfo: [Notification.Key.Task: task]
        )
    }

    /// Cancels the request.
    open func cancel() {
        guard let task = task else { return }

        task.cancel()

        NotificationCenter.default.post(
            name: Notification.Name.Task.DidCancel,
            object: self,
            userInfo: [Notification.Key.Task: task]
        )
    }

對(duì)應(yīng)的實(shí)現(xiàn)會(huì)發(fā)送對(duì)應(yīng)的通知而已

接下來(lái)是DataReuqest,作為請(qǐng)求的一種方式,繼承自request,是用來(lái)管理一個(gè)潛在的URLSessionDataTask.

// MARK: Helper Types

    struct Requestable: TaskConvertible {
        let urlRequest: URLRequest

        func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
            do {
                let urlRequest = try self.urlRequest.adapt(using: adapter)
                return queue.sync { session.dataTask(with: urlRequest) }
            } catch {
                throw AdaptError(error: error)
            }
        }
    }

    // MARK: Properties

    /// The request sent or to be sent to the server.
    open override var request: URLRequest? {
        if let request = super.request { return request }
        if let requestable = originalTask as? Requestable { return requestable.urlRequest }

        return nil
    }

    /// The progress of fetching the response data from the server for the request.
    open var progress: Progress { return dataDelegate.progress }

    var dataDelegate: DataTaskDelegate { return delegate as! DataTaskDelegate }

    // MARK: Stream

    /// Sets a closure to be called periodically during the lifecycle of the request as data is read from the server.
    ///
    /// This closure returns the bytes most recently received from the server, not including data from previous calls.
    /// If this closure is set, data will only be available within this closure, and will not be saved elsewhere. It is
    /// also important to note that the server data in any `Response` object will be `nil`.
    ///
    /// - parameter closure: The code to be executed periodically during the lifecycle of the request.
    ///
    /// - returns: The request.
    @discardableResult
    open func stream(closure: ((Data) -> Void)? = nil) -> Self {
        dataDelegate.dataStream = closure
        return self
    }

    // MARK: Progress

    /// Sets a closure to be called periodically during the lifecycle of the `Request` as data is read from the server.
    ///
    /// - parameter queue:   The dispatch queue to execute the closure on.
    /// - parameter closure: The code to be executed periodically as data is read from the server.
    ///
    /// - returns: The request.
    @discardableResult
    open func downloadProgress(queue: DispatchQueue = DispatchQueue.main, closure: @escaping ProgressHandler) -> Self {
        dataDelegate.progressHandler = (closure, queue)
        return self
    }

源碼并不是很復(fù)雜,主要分成幾個(gè)小的部分.請(qǐng)求,獲取請(qǐng)求urlRequest,通過(guò)Requestable判斷能夠發(fā)起請(qǐng)求,可以的話則發(fā)起請(qǐng)求,之后測(cè)試請(qǐng)求的過(guò)程中的季度,代理等,以及請(qǐng)求返回的數(shù)據(jù)
DownloadRequest稍微復(fù)雜一點(diǎn),因?yàn)槭窍螺d,所以無(wú)可避免的數(shù)據(jù)量會(huì)大一些,從而需要對(duì)暫土仓恢復(fù)等做個(gè)稍微復(fù)雜的處理

public struct DownloadOptions: OptionSet {
        /// Returns the raw bitmask value of the option and satisfies the `RawRepresentable` protocol.
        public let rawValue: UInt

        /// A `DownloadOptions` flag that creates intermediate directories for the destination URL if specified.
        public static let createIntermediateDirectories = DownloadOptions(rawValue: 1 << 0)

        /// A `DownloadOptions` flag that removes a previous file from the destination URL if specified.
        public static let removePreviousFile = DownloadOptions(rawValue: 1 << 1)

        /// Creates a `DownloadFileDestinationOptions` instance with the specified raw value.
        ///
        /// - parameter rawValue: The raw bitmask value for the option.
        ///
        /// - returns: A new log level instance.
        public init(rawValue: UInt) {
            self.rawValue = rawValue
        }
    }

下載方式的選擇,創(chuàng)建中間目錄,移除之前的文件等等,
相較于Requestable來(lái)說(shuō)Downloadable會(huì)稍微復(fù)雜點(diǎn),是否可以下載的話有對(duì)url的判斷,同樣還有對(duì)數(shù)據(jù)的判斷,

enum Downloadable: TaskConvertible {
        case request(URLRequest)
        case resumeData(Data)

        func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
            do {
                let task: URLSessionTask

                switch self {
                case let .request(urlRequest):
                    let urlRequest = try urlRequest.adapt(using: adapter)
                    task = queue.sync { session.downloadTask(with: urlRequest) }
                case let .resumeData(resumeData):
                    task = queue.sync { session.downloadTask(withResumeData: resumeData) }
                }

                return task
            } catch {
                throw AdaptError(error: error)
            }
        }
    }
/// The request sent or to be sent to the server.
    open override var request: URLRequest? {
        if let request = super.request { return request }

        if let downloadable = originalTask as? Downloadable, case let .request(urlRequest) = downloadable {
            return urlRequest
        }

        return nil
    }

    /// The resume data of the underlying download task if available after a failure.
    open var resumeData: Data? { return downloadDelegate.resumeData }

    /// The progress of downloading the response data from the server for the request.
    open var progress: Progress { return downloadDelegate.progress }

    var downloadDelegate: DownloadTaskDelegate { return delegate as! DownloadTaskDelegate }

    // MARK: State

    /// Cancels the request.
    open override func cancel() {
        downloadDelegate.downloadTask.cancel { self.downloadDelegate.resumeData = $0 }

        NotificationCenter.default.post(
            name: Notification.Name.Task.DidCancel,
            object: self,
            userInfo: [Notification.Key.Task: task as Any]
        )
    }

    // MARK: Progress

    /// Sets a closure to be called periodically during the lifecycle of the `Request` as data is read from the server.
    ///
    /// - parameter queue:   The dispatch queue to execute the closure on.
    /// - parameter closure: The code to be executed periodically as data is read from the server.
    ///
    /// - returns: The request.
    @discardableResult
    open func downloadProgress(queue: DispatchQueue = DispatchQueue.main, closure: @escaping ProgressHandler) -> Self {
        downloadDelegate.progressHandler = (closure, queue)
        return self
    }

    // MARK: Destination

    /// Creates a download file destination closure which uses the default file manager to move the temporary file to a
    /// file URL in the first available directory with the specified search path directory and search path domain mask.
    ///
    /// - parameter directory: The search path directory. `.DocumentDirectory` by default.
    /// - parameter domain:    The search path domain mask. `.UserDomainMask` by default.
    ///
    /// - returns: A download file destination closure.
    open class func suggestedDownloadDestination(
        for directory: FileManager.SearchPathDirectory = .documentDirectory,
        in domain: FileManager.SearchPathDomainMask = .userDomainMask)
        -> DownloadFileDestination
    {
        return { temporaryURL, response in
            let directoryURLs = FileManager.default.urls(for: directory, in: domain)

            if !directoryURLs.isEmpty {
                return (directoryURLs[0].appendingPathComponent(response.suggestedFilename!), [])
            }

            return (temporaryURL, [])
        }
    }

請(qǐng)求的方式一樣,當(dāng)然多了幾個(gè)內(nèi)容,下載失敗之后的可恢復(fù)數(shù)據(jù),建議下載的目的地等.但是大致上都是一樣的,至于后面的UploadRequest和StreamRequest也都是大致一樣的內(nèi)容而已.相較于只是細(xì)節(jié)上的一些優(yōu)化.

TaskDelegate部分
因?yàn)橛辛藃equest那么相對(duì)應(yīng)的有了task對(duì)象來(lái)管理這個(gè)request,同樣的,就產(chǎn)生了各種狀態(tài)或者進(jìn)度.,那么TaskDelegate就是task的各種協(xié)議
這塊相對(duì)來(lái)說(shuō)就很簡(jiǎn)單了主要的部分是TaskDelegate部分,其包含的主要內(nèi)容是

// MARK: Properties

    /// The serial operation queue used to execute all operations after the task completes.
    open let queue: OperationQueue

    /// The data returned by the server.
    public var data: Data? { return nil }

    /// The error generated throughout the lifecyle of the task.
    public var error: Error?

    var task: URLSessionTask? {
        didSet { reset() }
    }

    var initialResponseTime: CFAbsoluteTime?
    var credential: URLCredential?
    var metrics: AnyObject? // URLSessionTaskMetrics

    // MARK: Lifecycle

    init(task: URLSessionTask?) {
        self.task = task

        self.queue = {
            let operationQueue = OperationQueue()

            operationQueue.maxConcurrentOperationCount = 1
            operationQueue.isSuspended = true
            operationQueue.qualityOfService = .utility

            return operationQueue
        }()
    }

    func reset() {
        error = nil
        initialResponseTime = nil
    }

    // MARK: URLSessionTaskDelegate

    var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> URLRequest?)?
    var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?
    var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> InputStream?)?
    var taskDidCompleteWithError: ((URLSession, URLSessionTask, Error?) -> Void)?

有數(shù)據(jù),有各種回調(diào)的閉包,有錯(cuò)誤...
此外就是幾個(gè)協(xié)議方法

queue: OperationQueue 很明顯這是一個(gè)隊(duì)列,隊(duì)列中可以添加很多operation屋确。隊(duì)列是可以被暫停的纳击,通過(guò)把isSuspended設(shè)置為true就可以讓隊(duì)列中的所有operation暫停,直到isSuspended設(shè)置為false后攻臀,operation才會(huì)開(kāi)始執(zhí)行焕数。在Alamofire中,放入該隊(duì)列的operation有一下幾種情況:
    注意:該隊(duì)列會(huì)在任務(wù)完成之后把isSuspended設(shè)置為false
    一個(gè)Request的endTime刨啸,在任務(wù)完成后調(diào)用堡赔,就可以為Request設(shè)置請(qǐng)求結(jié)束時(shí)間
    response處理,Alamofire中的響應(yīng)回調(diào)是鏈?zhǔn)降纳枇砭褪前堰@些回調(diào)函數(shù)通過(guò)operation添加到隊(duì)列中善已,因此也保證了回調(diào)函數(shù)的訪問(wèn)順序是正確的
    上傳數(shù)據(jù)成功后,刪除臨時(shí)文件离例,這個(gè)后續(xù)的文章會(huì)解釋的
data: Data? 表示服務(wù)器返回的Data换团,這個(gè)可能為空
error: Error?表示該代理生命周期內(nèi)有可能出現(xiàn)的錯(cuò)誤,這一點(diǎn)很重要宫蛆,我們?cè)趯懸粋€(gè)代理或者manager的時(shí)候艘包,可以添加這么一個(gè)錯(cuò)誤屬性,專門抓取生命周期內(nèi)的錯(cuò)誤洒扎。
task: URLSessionTask? 表示一個(gè)task辑甜,對(duì)于本代理而言衰絮,task是很重要的一個(gè)屬性袍冷。
initialResponseTime: CFAbsoluteTime? 當(dāng)task是URLSessionDataTask時(shí),表示接收到數(shù)據(jù)的時(shí)間猫牡;當(dāng)task是URLSessionDownloadTask時(shí)胡诗,表示開(kāi)始寫數(shù)據(jù)的時(shí)間;當(dāng)task是URLSessionUploadTask時(shí)淌友,表示上傳數(shù)據(jù)的時(shí)間煌恢;
credential: URLCredential? 表示證書,如果給該代理設(shè)置了這個(gè)屬性震庭,在它里邊的證書驗(yàn)證方法中會(huì)備用到
metrics: AnyObject? apple提供了一個(gè)統(tǒng)計(jì)task信息的類URLSessionTaskMetrics,可以統(tǒng)計(jì)跟task相關(guān)的一些信息瑰抵,包括和task相關(guān)的所有事務(wù),task的開(kāi)始和結(jié)束時(shí)間器联,task的重定向的次數(shù)二汛。
@objc(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)
    func urlSession(
        _ session: URLSession,
        task: URLSessionTask,
        willPerformHTTPRedirection response: HTTPURLResponse,
        newRequest request: URLRequest,
        completionHandler: @escaping (URLRequest?) -> Void)
    {
        var redirectRequest: URLRequest? = request

        if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection {
            redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request)
        }

        completionHandler(redirectRequest)
    }

這個(gè)函數(shù)處理的問(wèn)題是請(qǐng)求重定向問(wèn)題

@objc(URLSession:task:didReceiveChallenge:completionHandler:)
    func urlSession(
        _ session: URLSession,
        task: URLSessionTask,
        didReceive challenge: URLAuthenticationChallenge,
        completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
    {
        var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
        var credential: URLCredential?

        if let taskDidReceiveChallenge = taskDidReceiveChallenge {
            (disposition, credential) = taskDidReceiveChallenge(session, task, challenge)
        } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
            let host = challenge.protectionSpace.host

            if
                let serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicy(forHost: host),
                let serverTrust = challenge.protectionSpace.serverTrust
            {
                if serverTrustPolicy.evaluate(serverTrust, forHost: host) {
                    disposition = .useCredential
                    credential = URLCredential(trust: serverTrust)
                } else {
                    disposition = .cancelAuthenticationChallenge
                }
            }
        } else {
            if challenge.previousFailureCount > 0 {
                disposition = .rejectProtectionSpace
            } else {
                credential = self.credential ?? session.configuration.urlCredentialStorage?.defaultCredential(for: challenge.protectionSpace)

                if credential != nil {
                    disposition = .useCredential
                }
            }
        }

        completionHandler(disposition, credential)
    }

改函數(shù)用于處理驗(yàn)證相關(guān)的事務(wù)
如果服務(wù)器需要驗(yàn)證客戶端的婿崭,我們只需要給TaskDelegate的 var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?賦值就行了。
這里有一個(gè)很重要的問(wèn)題肴颊,HTTPS并不會(huì)觸發(fā)上邊的回調(diào)函數(shù)氓栈,原因就是NSURLSession內(nèi)部有一個(gè)根證書,內(nèi)部會(huì)跟服務(wù)器的證書進(jìn)行驗(yàn)證婿着,如果服務(wù)器的證書是證書機(jī)構(gòu)頒發(fā)的話授瘦,就可以順利通過(guò)驗(yàn)證,否則會(huì)報(bào)錯(cuò)竟宋。

另一個(gè)很重要的問(wèn)題是提完,上邊的方法在何種情況下觸發(fā),按照apple的說(shuō)法URL Session Programming Guide,當(dāng)服務(wù)器響應(yīng)頭中包含WWW-Authenticate或者使用 proxy authentication TLS trust validation時(shí)丘侠,上邊的方法就會(huì)被觸發(fā)氯葬,其他情況下都不會(huì)觸發(fā)。

Alamofire是雙向驗(yàn)證的

雙向驗(yàn)證的過(guò)程:

當(dāng)服務(wù)器返回的challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust時(shí)婉陷,服務(wù)器提供了一個(gè)服務(wù)器信任的證書帚称,但這個(gè)證書也僅僅是服務(wù)器自己信任的,攻擊者完全可以提供一個(gè)證書騙過(guò)客戶端秽澳,基于這個(gè)問(wèn)題闯睹,客戶端需要驗(yàn)證服務(wù)端的證書
Alamofire中通過(guò)ServerTrustPolicy.swift這個(gè)類來(lái)驗(yàn)證證書,大概過(guò)程就是拿本地的證書跟服務(wù)端返回的證書進(jìn)行對(duì)比担神,如果客戶端存在相同的證書就表示通過(guò)楼吃,這個(gè)過(guò)程我會(huì)在ServerTrustPolicy.swift那篇文章中給出詳細(xì)的解答

因此,客戶端和服務(wù)端要建立SSL只需要兩步就行了:

服務(wù)端返回WWW-Authenticate響應(yīng)頭妄讯,并返回自己信任證書
客戶端驗(yàn)證證書孩锡,然后用證書中的公鑰把數(shù)據(jù)加密后發(fā)送給服務(wù)端
@objc(URLSession:task:needNewBodyStream:)
    func urlSession(
        _ session: URLSession,
        task: URLSessionTask,
        needNewBodyStream completionHandler: @escaping (InputStream?) -> Void)
    {
        var bodyStream: InputStream?

        if let taskNeedNewBodyStream = taskNeedNewBodyStream {
            bodyStream = taskNeedNewBodyStream(session, task)
        }

        completionHandler(bodyStream)
    }

當(dāng)給task的Request提供一個(gè)body stream時(shí)才會(huì)調(diào)用,我們不需要關(guān)心這個(gè)方法亥贸,即使我們通過(guò)fileURL或者NSData上傳數(shù)據(jù)時(shí)躬窜,該函數(shù)也不會(huì)被調(diào)用,使用場(chǎng)景很少。

@objc(URLSession:task:didCompleteWithError:)
    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        if let taskDidCompleteWithError = taskDidCompleteWithError {
            taskDidCompleteWithError(session, task, error)
        } else {
            if let error = error {
                if self.error == nil { self.error = error }

                if
                    let downloadDelegate = self as? DownloadTaskDelegate,
                    let resumeData = (error as NSError).userInfo[NSURLSessionDownloadTaskResumeData] as? Data
                {
                    downloadDelegate.resumeData = resumeData
                }
            }

            queue.isSuspended = false
        }
    }

該函數(shù)在請(qǐng)求完成后被調(diào)用,值得注意的是error不為nil的情況飞傀,除了給自身的error屬性賦值外,針對(duì)下載任務(wù)做了特殊處理默垄,就是把當(dāng)前已經(jīng)下載的數(shù)據(jù)保存在downloadDelegate.resumeData中,有點(diǎn)像斷點(diǎn)下載甚纲。

接下來(lái)的DataTaskDelegate繼承自TaskDelegate,實(shí)現(xiàn)了URLSessionDataDelegate協(xié)議

var dataTask: URLSessionDataTask { return task as! URLSessionDataTask }

    override var data: Data? {
        if dataStream != nil {
            return nil
        } else {
            return mutableData
        }
    }

    var progress: Progress
    var progressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)?

    var dataStream: ((_ data: Data) -> Void)?

    private var totalBytesReceived: Int64 = 0
    private var mutableData: Data

    private var expectedContentLength: Int64?
dataTask: URLSessionDataTask DataTaskDelegate管理URLSessionDataTask
data: Data? 同樣是返回Data口锭,但這里有一點(diǎn)不同,如果定義了dataStream方法的話介杆,這個(gè)data返回為nil
progress: Progress 進(jìn)度
progressHandler 這不是函數(shù)鹃操,是一個(gè)元組况既,什么時(shí)候調(diào)用,在下邊的方法中給出說(shuō)明
dataStream 自定義的數(shù)據(jù)處理函數(shù)
totalBytesReceived 已經(jīng)接受的數(shù)據(jù)
mutableData 保存數(shù)據(jù)的容器
expectedContentLength 需要接受的數(shù)據(jù)的總大小

有四個(gè)函數(shù)

var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> URLSession.ResponseDisposition)?
    var dataTaskDidBecomeDownloadTask: ((URLSession, URLSessionDataTask, URLSessionDownloadTask) -> Void)?
    var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)?
    var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)?

第一個(gè)

func urlSession(
        _ session: URLSession,
        dataTask: URLSessionDataTask,
        didReceive response: URLResponse,
        completionHandler: @escaping (URLSession.ResponseDisposition) -> Void)
    {
        var disposition: URLSession.ResponseDisposition = .allow

        expectedContentLength = response.expectedContentLength

        if let dataTaskDidReceiveResponse = dataTaskDidReceiveResponse {
            disposition = dataTaskDidReceiveResponse(session, dataTask, response)
        }

        completionHandler(disposition)
    }

當(dāng)收到服務(wù)端的響應(yīng)后组民,該方法被觸發(fā)棒仍。在這個(gè)函數(shù)中,我們能夠獲取到和數(shù)據(jù)相關(guān)的一些參數(shù)臭胜,大家可以想象成響應(yīng)頭莫其。Alamofire中給出了一個(gè)函數(shù)dataTaskDidReceiveResponse,我們可以利用這個(gè)函數(shù)控制是不是要繼續(xù)獲取數(shù)據(jù)耸三,默認(rèn)是.allow乱陡。

第二個(gè)

func urlSession(
        _ session: URLSession,
        dataTask: URLSessionDataTask,
        didBecome downloadTask: URLSessionDownloadTask)
    {
        dataTaskDidBecomeDownloadTask?(session, dataTask, downloadTask)
    }

當(dāng)我們把URLSession.ResponseDisposition設(shè)置成becomeDownload就會(huì)觸發(fā)上邊的第二個(gè)函數(shù),在函數(shù)中會(huì)提供一個(gè)新的downloadTask仪壮。這就給我們一個(gè)把dataTask轉(zhuǎn)換成downloadTask的機(jī)會(huì)

第三個(gè)

func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
        if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }

        if let dataTaskDidReceiveData = dataTaskDidReceiveData {
            dataTaskDidReceiveData(session, dataTask, data)
        } else {
            if let dataStream = dataStream {
                dataStream(data)
            } else {
                mutableData.append(data)
            }

            let bytesReceived = Int64(data.count)
            totalBytesReceived += bytesReceived
            let totalBytesExpected = dataTask.response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown

            progress.totalUnitCount = totalBytesExpected
            progress.completedUnitCount = totalBytesReceived

            if let progressHandler = progressHandler {
                progressHandler.queue.async { progressHandler.closure(self.progress) }
            }
        }
    }

Alamofire把數(shù)據(jù)放入對(duì)象中的過(guò)程,也是下載的核心方法..

第四個(gè)

func urlSession(
        _ session: URLSession,
        dataTask: URLSessionDataTask,
        willCacheResponse proposedResponse: CachedURLResponse,
        completionHandler: @escaping (CachedURLResponse?) -> Void)
    {
        var cachedResponse: CachedURLResponse? = proposedResponse

        if let dataTaskWillCacheResponse = dataTaskWillCacheResponse {
            cachedResponse = dataTaskWillCacheResponse(session, dataTask, proposedResponse)
        }

        completionHandler(cachedResponse)
    }

該函數(shù)用于處理是否需要緩存響應(yīng)憨颠,Alamofire默認(rèn)是緩存這些response的,但是每次發(fā)請(qǐng)求积锅,它不會(huì)再緩存中讀取爽彤。
至于后續(xù)的DownloadTaskDelegate...基本都是差不多的實(shí)現(xiàn),不通的類型處理的思路稍微不同...

Response部分

首先是默認(rèn)的DataResponse
DefaultDataResponse

public struct DefaultDataResponse {
    /// The URL request sent to the server.
    public let request: URLRequest?

    /// The server's response to the URL request.
    public let response: HTTPURLResponse?

    /// The data returned by the server.
    public let data: Data?

    /// The error encountered while executing or validating the request.
    public let error: Error?

    /// The timeline of the complete lifecycle of the request.
    public let timeline: Timeline

    var _metrics: AnyObject?

    /// Creates a `DefaultDataResponse` instance from the specified parameters.
    ///
    /// - Parameters:
    ///   - request:  The URL request sent to the server.
    ///   - response: The server's response to the URL request.
    ///   - data:     The data returned by the server.
    ///   - error:    The error encountered while executing or validating the request.
    ///   - timeline: The timeline of the complete lifecycle of the request. `Timeline()` by default.
    ///   - metrics:  The task metrics containing the request / response statistics. `nil` by default.
    public init(
        request: URLRequest?,
        response: HTTPURLResponse?,
        data: Data?,
        error: Error?,
        timeline: Timeline = Timeline(),
        metrics: AnyObject? = nil)
    {
        self.request = request
        self.response = response
        self.data = data
        self.error = error
        self.timeline = timeline
    }
}

簡(jiǎn)單的理解
request:URLRequest,本次訪問(wèn)數(shù)據(jù)的urlRequest,包含了請(qǐng)求的地址,方式...
response:服務(wù)器的響應(yīng),有返回的狀態(tài)碼,頭部信息等
data:服務(wù)器返回的數(shù)據(jù),可能為空
error:錯(cuò)誤的類型
timeline:請(qǐng)求的完整生命周期的時(shí)間
通過(guò)一個(gè)默認(rèn)的響應(yīng)結(jié)果可以看到大致其包含的信息有狀態(tài)和數(shù)據(jù)(錯(cuò)誤)等,

public struct DataResponse<Value> {
    /// The URL request sent to the server.
    public let request: URLRequest?

    /// The server's response to the URL request.
    public let response: HTTPURLResponse?

    /// The data returned by the server.
    public let data: Data?

    /// The result of response serialization.
    public let result: Result<Value>

    /// The timeline of the complete lifecycle of the request.
    public let timeline: Timeline

    /// Returns the associated value of the result if it is a success, `nil` otherwise.
    public var value: Value? { return result.value }

    /// Returns the associated error value if the result if it is a failure, `nil` otherwise.
    public var error: Error? { return result.error }

    var _metrics: AnyObject?

    /// Creates a `DataResponse` instance with the specified parameters derived from response serialization.
    ///
    /// - parameter request:  The URL request sent to the server.
    /// - parameter response: The server's response to the URL request.
    /// - parameter data:     The data returned by the server.
    /// - parameter result:   The result of response serialization.
    /// - parameter timeline: The timeline of the complete lifecycle of the `Request`. Defaults to `Timeline()`.
    ///
    /// - returns: The new `DataResponse` instance.
    public init(
        request: URLRequest?,
        response: HTTPURLResponse?,
        data: Data?,
        result: Result<Value>,
        timeline: Timeline = Timeline())
    {
        self.request = request
        self.response = response
        self.data = data
        self.result = result
        self.timeline = timeline
    }
}

dataResponse部分,基本都是一樣的內(nèi)容,需要看的是其擴(kuò)展的部分

    /// Evaluates the specified closure when the result of this `DataResponse` is a success, passing the unwrapped
    /// result value as a parameter.
    ///
    /// Use the `map` method with a closure that does not throw. For example:
    ///
    ///     let possibleData: DataResponse<Data> = ...
    ///     let possibleInt = possibleData.map { $0.count }
    ///
    /// - parameter transform: A closure that takes the success value of the instance's result.
    ///
    /// - returns: A `DataResponse` whose result wraps the value returned by the given closure. If this instance's
    ///            result is a failure, returns a response wrapping the same failure.
    public func map<T>(_ transform: (Value) -> T) -> DataResponse<T> {
        var response = DataResponse<T>(
            request: request,
            response: self.response,
            data: data,
            result: result.map(transform),
            timeline: timeline
        )

        response._metrics = _metrics

        return response
    }

    /// Evaluates the given closure when the result of this `DataResponse` is a success, passing the unwrapped result
    /// value as a parameter.
    ///
    /// Use the `flatMap` method with a closure that may throw an error. For example:
    ///
    ///     let possibleData: DataResponse<Data> = ...
    ///     let possibleObject = possibleData.flatMap {
    ///         try JSONSerialization.jsonObject(with: $0)
    ///     }
    ///
    /// - parameter transform: A closure that takes the success value of the instance's result.
    ///
    /// - returns: A success or failure `DataResponse` depending on the result of the given closure. If this instance's
    ///            result is a failure, returns the same failure.
    public func flatMap<T>(_ transform: (Value) throws -> T) -> DataResponse<T> {
        var response = DataResponse<T>(
            request: request,
            response: self.response,
            data: data,
            result: result.flatMap(transform),
            timeline: timeline
        )

        response._metrics = _metrics

        return response
    }
}

主要的是兩個(gè)過(guò)濾及序列化的方式
再接下來(lái)的DownloadResponse..也差不多是一樣的....

ResponseSerialization部分
有了服務(wù)器的返回?cái)?shù)據(jù)那么肯定就有解析數(shù)據(jù)的,
至于是什么樣的序列化方式...

public protocol DataResponseSerializerProtocol {
    /// The type of serialized object to be created by this `DataResponseSerializerType`.
    associatedtype SerializedObject

    /// A closure used by response handlers that takes a request, response, data and error and returns a result.
    var serializeResponse: (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result<SerializedObject> { get }
}

DataResponseSerializer協(xié)議,返回的是一個(gè)SerializedObject的對(duì)象

首先是DataResponseSerializer

public struct DataResponseSerializer<Value>: DataResponseSerializerProtocol {
    /// The type of serialized object to be created by this `DataResponseSerializer`.
    public typealias SerializedObject = Value

    /// A closure used by response handlers that takes a request, response, data and error and returns a result.
    public var serializeResponse: (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result<Value>

    /// Initializes the `ResponseSerializer` instance with the given serialize response closure.
    ///
    /// - parameter serializeResponse: The closure used to serialize the response.
    ///
    /// - returns: The new generic response serializer instance.
    public init(serializeResponse: @escaping (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result<Value>) {
        self.serializeResponse = serializeResponse
    }
}

沒(méi)有序列化的,直接返回HTTPResponse

/// The type in which all download response serializers must conform to in order to serialize a response.
public protocol DownloadResponseSerializerProtocol {
    /// The type of serialized object to be created by this `DownloadResponseSerializerType`.
    associatedtype SerializedObject

    /// A closure used by response handlers that takes a request, response, url and error and returns a result.
    var serializeResponse: (URLRequest?, HTTPURLResponse?, URL?, Error?) -> Result<SerializedObject> { get }
}

// MARK: -

/// A generic `DownloadResponseSerializerType` used to serialize a request, response, and data into a serialized object.
public struct DownloadResponseSerializer<Value>: DownloadResponseSerializerProtocol {
    /// The type of serialized object to be created by this `DownloadResponseSerializer`.
    public typealias SerializedObject = Value

    /// A closure used by response handlers that takes a request, response, url and error and returns a result.
    public var serializeResponse: (URLRequest?, HTTPURLResponse?, URL?, Error?) -> Result<Value>

    /// Initializes the `ResponseSerializer` instance with the given serialize response closure.
    ///
    /// - parameter serializeResponse: The closure used to serialize the response.
    ///
    /// - returns: The new generic response serializer instance.
    public init(serializeResponse: @escaping (URLRequest?, HTTPURLResponse?, URL?, Error?) -> Result<Value>) {
        self.serializeResponse = serializeResponse
    }
}

跟DataResponseSerializer類似

    /// Adds a handler to be called once the request has finished.
    ///
    /// - parameter queue:             The queue on which the completion handler is dispatched.
    /// - parameter completionHandler: The code to be executed once the request has finished.
    ///
    /// - returns: The request.
    @discardableResult
    public func response(queue: DispatchQueue? = nil, completionHandler: @escaping (DefaultDataResponse) -> Void) -> Self {
        delegate.queue.addOperation {
            (queue ?? DispatchQueue.main).async {
                var dataResponse = DefaultDataResponse(
                    request: self.request,
                    response: self.response,
                    data: self.delegate.data,
                    error: self.delegate.error,
                    timeline: self.timeline
                )

                dataResponse.add(self.delegate.metrics)

                completionHandler(dataResponse)
            }
        }

        return self
    }

    /// Adds a handler to be called once the request has finished.
    ///
    /// - parameter queue:              The queue on which the completion handler is dispatched.
    /// - parameter responseSerializer: The response serializer responsible for serializing the request, response,
    ///                                 and data.
    /// - parameter completionHandler:  The code to be executed once the request has finished.
    ///
    /// - returns: The request.
    @discardableResult
    public func response<T: DataResponseSerializerProtocol>(
        queue: DispatchQueue? = nil,
        responseSerializer: T,
        completionHandler: @escaping (DataResponse<T.SerializedObject>) -> Void)
        -> Self
    {
        delegate.queue.addOperation {
            let result = responseSerializer.serializeResponse(
                self.request,
                self.response,
                self.delegate.data,
                self.delegate.error
            )

            var dataResponse = DataResponse<T.SerializedObject>(
                request: self.request,
                response: self.response,
                data: self.delegate.data,
                result: result,
                timeline: self.timeline
            )

            dataResponse.add(self.delegate.metrics)

            (queue ?? DispatchQueue.main).async { completionHandler(dataResponse) }
        }

        return self
    }
}

兩個(gè)方式大致是差不多的,都是給請(qǐng)求添加一個(gè)func,實(shí)現(xiàn)請(qǐng)求完成之后的回調(diào),
第一個(gè)使用的是默認(rèn)的DefaultDataResponse類型的結(jié)果,第二個(gè)則是自定義的實(shí)現(xiàn)DataResponseSerializerProtocol的結(jié)果,

結(jié)果序列化成Data的

// MARK: - Data

extension Request {
    /// Returns a result data type that contains the response data as-is.
    ///
    /// - parameter response: The response from the server.
    /// - parameter data:     The data returned from the server.
    /// - parameter error:    The error already encountered if it exists.
    ///
    /// - returns: The result data type.
    public static func serializeResponseData(response: HTTPURLResponse?, data: Data?, error: Error?) -> Result<Data> {
        guard error == nil else { return .failure(error!) }

        if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success(Data()) }

        guard let validData = data else {
            return .failure(AFError.responseSerializationFailed(reason: .inputDataNil))
        }

        return .success(validData)
    }
}

extension DataRequest {
    /// Creates a response serializer that returns the associated data as-is.
    ///
    /// - returns: A data response serializer.
    public static func dataResponseSerializer() -> DataResponseSerializer<Data> {
        return DataResponseSerializer { _, response, data, error in
            return Request.serializeResponseData(response: response, data: data, error: error)
        }
    }

    /// Adds a handler to be called once the request has finished.
    ///
    /// - parameter completionHandler: The code to be executed once the request has finished.
    ///
    /// - returns: The request.
    @discardableResult
    public func responseData(
        queue: DispatchQueue? = nil,
        completionHandler: @escaping (DataResponse<Data>) -> Void)
        -> Self
    {
        return response(
            queue: queue,
            responseSerializer: DataRequest.dataResponseSerializer(),
            completionHandler: completionHandler
        )
    }
}

結(jié)果序列化成字符串

// MARK: - String

extension Request {
    /// Returns a result string type initialized from the response data with the specified string encoding.
    ///
    /// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the server
    ///                       response, falling back to the default HTTP default character set, ISO-8859-1.
    /// - parameter response: The response from the server.
    /// - parameter data:     The data returned from the server.
    /// - parameter error:    The error already encountered if it exists.
    ///
    /// - returns: The result data type.
    public static func serializeResponseString(
        encoding: String.Encoding?,
        response: HTTPURLResponse?,
        data: Data?,
        error: Error?)
        -> Result<String>
    {
        guard error == nil else { return .failure(error!) }

        if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success("") }

        guard let validData = data else {
            return .failure(AFError.responseSerializationFailed(reason: .inputDataNil))
        }

        var convertedEncoding = encoding

        if let encodingName = response?.textEncodingName as CFString!, convertedEncoding == nil {
            convertedEncoding = String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(
                CFStringConvertIANACharSetNameToEncoding(encodingName))
            )
        }

        let actualEncoding = convertedEncoding ?? String.Encoding.isoLatin1

        if let string = String(data: validData, encoding: actualEncoding) {
            return .success(string)
        } else {
            return .failure(AFError.responseSerializationFailed(reason: .stringSerializationFailed(encoding: actualEncoding)))
        }
    }
}

extension DataRequest {
    /// Creates a response serializer that returns a result string type initialized from the response data with
    /// the specified string encoding.
    ///
    /// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the server
    ///                       response, falling back to the default HTTP default character set, ISO-8859-1.
    ///
    /// - returns: A string response serializer.
    public static func stringResponseSerializer(encoding: String.Encoding? = nil) -> DataResponseSerializer<String> {
        return DataResponseSerializer { _, response, data, error in
            return Request.serializeResponseString(encoding: encoding, response: response, data: data, error: error)
        }
    }

    /// Adds a handler to be called once the request has finished.
    ///
    /// - parameter encoding:          The string encoding. If `nil`, the string encoding will be determined from the
    ///                                server response, falling back to the default HTTP default character set,
    ///                                ISO-8859-1.
    /// - parameter completionHandler: A closure to be executed once the request has finished.
    ///
    /// - returns: The request.
    @discardableResult
    public func responseString(
        queue: DispatchQueue? = nil,
        encoding: String.Encoding? = nil,
        completionHandler: @escaping (DataResponse<String>) -> Void)
        -> Self
    {
        return response(
            queue: queue,
            responseSerializer: DataRequest.stringResponseSerializer(encoding: encoding),
            completionHandler: completionHandler
        )
    }
}

結(jié)果序列化成JSON

// MARK: - JSON

extension Request {
    /// Returns a JSON object contained in a result type constructed from the response data using `JSONSerialization`
    /// with the specified reading options.
    ///
    /// - parameter options:  The JSON serialization reading options. Defaults to `.allowFragments`.
    /// - parameter response: The response from the server.
    /// - parameter data:     The data returned from the server.
    /// - parameter error:    The error already encountered if it exists.
    ///
    /// - returns: The result data type.
    public static func serializeResponseJSON(
        options: JSONSerialization.ReadingOptions,
        response: HTTPURLResponse?,
        data: Data?,
        error: Error?)
        -> Result<Any>
    {
        guard error == nil else { return .failure(error!) }

        if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success(NSNull()) }

        guard let validData = data, validData.count > 0 else {
            return .failure(AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength))
        }

        do {
            let json = try JSONSerialization.jsonObject(with: validData, options: options)
            return .success(json)
        } catch {
            return .failure(AFError.responseSerializationFailed(reason: .jsonSerializationFailed(error: error)))
        }
    }
}

extension DataRequest {
    /// Creates a response serializer that returns a JSON object result type constructed from the response data using
    /// `JSONSerialization` with the specified reading options.
    ///
    /// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`.
    ///
    /// - returns: A JSON object response serializer.
    public static func jsonResponseSerializer(
        options: JSONSerialization.ReadingOptions = .allowFragments)
        -> DataResponseSerializer<Any>
    {
        return DataResponseSerializer { _, response, data, error in
            return Request.serializeResponseJSON(options: options, response: response, data: data, error: error)
        }
    }

    /// Adds a handler to be called once the request has finished.
    ///
    /// - parameter options:           The JSON serialization reading options. Defaults to `.allowFragments`.
    /// - parameter completionHandler: A closure to be executed once the request has finished.
    ///
    /// - returns: The request.
    @discardableResult
    public func responseJSON(
        queue: DispatchQueue? = nil,
        options: JSONSerialization.ReadingOptions = .allowFragments,
        completionHandler: @escaping (DataResponse<Any>) -> Void)
        -> Self
    {
        return response(
            queue: queue,
            responseSerializer: DataRequest.jsonResponseSerializer(options: options),
            completionHandler: completionHandler
        )
    }
}

結(jié)果序列化成Any

// MARK: - Property List

extension Request {
    /// Returns a plist object contained in a result type constructed from the response data using
    /// `PropertyListSerialization` with the specified reading options.
    ///
    /// - parameter options:  The property list reading options. Defaults to `[]`.
    /// - parameter response: The response from the server.
    /// - parameter data:     The data returned from the server.
    /// - parameter error:    The error already encountered if it exists.
    ///
    /// - returns: The result data type.
    public static func serializeResponsePropertyList(
        options: PropertyListSerialization.ReadOptions,
        response: HTTPURLResponse?,
        data: Data?,
        error: Error?)
        -> Result<Any>
    {
        guard error == nil else { return .failure(error!) }

        if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success(NSNull()) }

        guard let validData = data, validData.count > 0 else {
            return .failure(AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength))
        }

        do {
            let plist = try PropertyListSerialization.propertyList(from: validData, options: options, format: nil)
            return .success(plist)
        } catch {
            return .failure(AFError.responseSerializationFailed(reason: .propertyListSerializationFailed(error: error)))
        }
    }
}

extension DataRequest {
    /// Creates a response serializer that returns an object constructed from the response data using
    /// `PropertyListSerialization` with the specified reading options.
    ///
    /// - parameter options: The property list reading options. Defaults to `[]`.
    ///
    /// - returns: A property list object response serializer.
    public static func propertyListResponseSerializer(
        options: PropertyListSerialization.ReadOptions = [])
        -> DataResponseSerializer<Any>
    {
        return DataResponseSerializer { _, response, data, error in
            return Request.serializeResponsePropertyList(options: options, response: response, data: data, error: error)
        }
    }

    /// Adds a handler to be called once the request has finished.
    ///
    /// - parameter options:           The property list reading options. Defaults to `[]`.
    /// - parameter completionHandler: A closure to be executed once the request has finished.
    ///
    /// - returns: The request.
    @discardableResult
    public func responsePropertyList(
        queue: DispatchQueue? = nil,
        options: PropertyListSerialization.ReadOptions = [],
        completionHandler: @escaping (DataResponse<Any>) -> Void)
        -> Self
    {
        return response(
            queue: queue,
            responseSerializer: DataRequest.propertyListResponseSerializer(options: options),
            completionHandler: completionHandler
        )
    }
}

Result部分
結(jié)果已經(jīng)序列化或者自定成想要的格式了,那么對(duì)應(yīng)的一個(gè)請(qǐng)求就完成了本次相應(yīng)的完整的請(qǐng)求,那么對(duì)應(yīng)的回調(diào)結(jié)果就應(yīng)該返回序列化之后的結(jié)果
響應(yīng)有成功,有失敗,相對(duì)應(yīng)的就有了兩種不同的result

public enum Result<Value> {
    case success(Value)
    case failure(Error)

    /// Returns `true` if the result is a success, `false` otherwise.
    public var isSuccess: Bool {
        switch self {
        case .success:
            return true
        case .failure:
            return false
        }
    }

    /// Returns `true` if the result is a failure, `false` otherwise.
    public var isFailure: Bool {
        return !isSuccess
    }

    /// Returns the associated value if the result is a success, `nil` otherwise.
    public var value: Value? {
        switch self {
        case .success(let value):
            return value
        case .failure:
            return nil
        }
    }

    /// Returns the associated error value if the result is a failure, `nil` otherwise.
    public var error: Error? {
        switch self {
        case .success:
            return nil
        case .failure(let error):
            return error
        }
    }
}

跟上面說(shuō)的一樣,首先是枚舉,成功或者失敗,另外這里用了泛型
泛型的寫法是類似這樣的:<T>,在<和>之間聲明一種類型缚陷,這個(gè)T知識(shí)象征性的适篙,在賦值的時(shí)候,可以是任何類型
我們從上面講到的responseJSON中可以看到

@discardableResult
    public func responseJSON(
        queue: DispatchQueue? = nil,
        options: JSONSerialization.ReadingOptions = .allowFragments,
        completionHandler: @escaping (DataResponse<Any>) -> Void)
        -> Self
    {
        return response(
            queue: queue,
            responseSerializer: DataRequest.jsonResponseSerializer(options: options),
            completionHandler: completionHandler
        )
    }

上邊的這個(gè)函數(shù)的主要目的是把請(qǐng)求成功后的結(jié)果序列化為JSON,completionHandler函數(shù)的參數(shù)類型為DataResponse<Any>箫爷,其中的Any就會(huì)傳遞給Result嚷节,也就是Result<Any>。

那么問(wèn)題來(lái)了虎锚,不是把數(shù)據(jù)解析成JSON了嗎硫痰?為什么要返回Any類型呢?json本質(zhì)上很類似于JavaScript中的對(duì)象和數(shù)組窜护。JSONSerialization.jsonObject返回的類型是Any效斑,這是因?yàn)榻馕龊蟮臄?shù)據(jù)有可能是數(shù)組,也有可能是字典柄慰。當(dāng)然如果不是這兩種格式的數(shù)據(jù)鳍悠,使用JSONSerialization.jsonObject解析會(huì)拋出異常税娜。

另外,result添加了一些屬性方便應(yīng)用使用...總起來(lái)說(shuō)坐搔,Result是一個(gè)比較簡(jiǎn)單的封裝。

差不多一次網(wǎng)絡(luò)請(qǐng)求的整個(gè)過(guò)程都已經(jīng)解析完畢了,那么我們肯定不能這個(gè)松散的,我們需要一個(gè)會(huì)話管理來(lái)控制每一個(gè)請(qǐng)求,控制并發(fā),控制進(jìn)度等.

SessionManager部分

大的來(lái)說(shuō),這是一個(gè)整合,把整個(gè)請(qǐng)求過(guò)程封裝成API的方式方便我們調(diào)用,同時(shí)作為一個(gè)會(huì)話管理,它也負(fù)責(zé)著并發(fā)等復(fù)雜處理

open static let defaultHTTPHeaders: HTTPHeaders = {
        // Accept-Encoding HTTP Header; see https://tools.ietf.org/html/rfc7230#section-4.2.3
        let acceptEncoding: String = "gzip;q=1.0, compress;q=0.5"

        // Accept-Language HTTP Header; see https://tools.ietf.org/html/rfc7231#section-5.3.5
        let acceptLanguage = Locale.preferredLanguages.prefix(6).enumerated().map { index, languageCode in
            let quality = 1.0 - (Double(index) * 0.1)
            return "\(languageCode);q=\(quality)"
        }.joined(separator: ", ")

        // User-Agent Header; see https://tools.ietf.org/html/rfc7231#section-5.5.3
        // Example: `iOS Example/1.0 (org.alamofire.iOS-Example; build:1; iOS 10.0.0) Alamofire/4.0.0`
        let userAgent: String = {
            if let info = Bundle.main.infoDictionary {
                let executable = info[kCFBundleExecutableKey as String] as? String ?? "Unknown"
                let bundle = info[kCFBundleIdentifierKey as String] as? String ?? "Unknown"
                let appVersion = info["CFBundleShortVersionString"] as? String ?? "Unknown"
                let appBuild = info[kCFBundleVersionKey as String] as? String ?? "Unknown"

                let osNameVersion: String = {
                    let version = ProcessInfo.processInfo.operatingSystemVersion
                    let versionString = "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)"

                    let osName: String = {
                        #if os(iOS)
                            return "iOS"
                        #elseif os(watchOS)
                            return "watchOS"
                        #elseif os(tvOS)
                            return "tvOS"
                        #elseif os(macOS)
                            return "OS X"
                        #elseif os(Linux)
                            return "Linux"
                        #else
                            return "Unknown"
                        #endif
                    }()

                    return "\(osName) \(versionString)"
                }()

                let alamofireVersion: String = {
                    guard
                        let afInfo = Bundle(for: SessionManager.self).infoDictionary,
                        let build = afInfo["CFBundleShortVersionString"]
                    else { return "Unknown" }

                    return "Alamofire/\(build)"
                }()

                return "\(executable)/\(appVersion) (\(bundle); build:\(appBuild); \(osNameVersion)) \(alamofireVersion)"
            }

            return "Alamofire"
        }()

        return [
            "Accept-Encoding": acceptEncoding,
            "Accept-Language": acceptLanguage,
            "User-Agent": userAgent
        ]
    }()

上面的內(nèi)容是一個(gè)默認(rèn)的defaultHTTPHeaders

open static let `default`: SessionManager = {
        let configuration = URLSessionConfiguration.default
        configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders

        return SessionManager(configuration: configuration)
    }()

返回一個(gè)默認(rèn)的會(huì)話管理單例

@discardableResult
    open func request(
        _ url: URLConvertible,
        method: HTTPMethod = .get,
        parameters: Parameters? = nil,
        encoding: ParameterEncoding = URLEncoding.default,
        headers: HTTPHeaders? = nil)
        -> DataRequest
    {
        var originalRequest: URLRequest?

        do {
            originalRequest = try URLRequest(url: url, method: method, headers: headers)
            let encodedURLRequest = try encoding.encode(originalRequest!, with: parameters)
            return request(encodedURLRequest)
        } catch {
            return request(originalRequest, failedWith: error)
        }
    }

請(qǐng)求函數(shù),傳入url,參數(shù),請(qǐng)求方式等.調(diào)用請(qǐng)求,并返回請(qǐng)求DataRequest,

/// Creates a `DataRequest` to retrieve the contents of a URL based on the specified `urlRequest`.
    ///
    /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
    ///
    /// - parameter urlRequest: The URL request.
    ///
    /// - returns: The created `DataRequest`.
    open func request(_ urlRequest: URLRequestConvertible) -> DataRequest {
        var originalRequest: URLRequest?

        do {
            originalRequest = try urlRequest.asURLRequest()
            let originalTask = DataRequest.Requestable(urlRequest: originalRequest!)

            let task = try originalTask.task(session: session, adapter: adapter, queue: queue)
            let request = DataRequest(session: session, requestTask: .data(originalTask, task))

            delegate[task] = request

            if startRequestsImmediately { request.resume() }

            return request
        } catch {
            return request(originalRequest, failedWith: error)
        }
    }

根據(jù)request 添加響應(yīng)的delegate,隊(duì)列,并resume發(fā)起請(qǐng)求,同時(shí)返回DataRequest

 private func request(_ urlRequest: URLRequest?, failedWith error: Error) -> DataRequest {
        var requestTask: Request.RequestTask = .data(nil, nil)

        if let urlRequest = urlRequest {
            let originalTask = DataRequest.Requestable(urlRequest: urlRequest)
            requestTask = .data(originalTask, nil)
        }

        let underlyingError = error.underlyingAdaptError ?? error
        let request = DataRequest(session: session, requestTask: requestTask, error: underlyingError)

        if let retrier = retrier, error is AdaptError {
            allowRetrier(retrier, toRetry: request, with: underlyingError)
        } else {
            if startRequestsImmediately { request.resume() }
        }

        return request
    }

同樣的請(qǐng)求,是在alamofire嘗試編碼失敗之后直接發(fā)起請(qǐng)求

// MARK: - Download Request

    // MARK: URL Request

    /// Creates a `DownloadRequest` to retrieve the contents the specified `url`, `method`, `parameters`, `encoding`,
    /// `headers` and save them to the `destination`.
    ///
    /// If `destination` is not specified, the contents will remain in the temporary location determined by the
    /// underlying URL session.
    ///
    /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
    ///
    /// - parameter url:         The URL.
    /// - parameter method:      The HTTP method. `.get` by default.
    /// - parameter parameters:  The parameters. `nil` by default.
    /// - parameter encoding:    The parameter encoding. `URLEncoding.default` by default.
    /// - parameter headers:     The HTTP headers. `nil` by default.
    /// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default.
    ///
    /// - returns: The created `DownloadRequest`.
    @discardableResult
    open func download(
        _ url: URLConvertible,
        method: HTTPMethod = .get,
        parameters: Parameters? = nil,
        encoding: ParameterEncoding = URLEncoding.default,
        headers: HTTPHeaders? = nil,
        to destination: DownloadRequest.DownloadFileDestination? = nil)
        -> DownloadRequest
    {
        do {
            let urlRequest = try URLRequest(url: url, method: method, headers: headers)
            let encodedURLRequest = try encoding.encode(urlRequest, with: parameters)
            return download(encodedURLRequest, to: destination)
        } catch {
            return download(nil, to: destination, failedWith: error)
        }
    }

download,類似于data,通過(guò)地址,參數(shù),方式等創(chuàng)建一個(gè)DownloadRequest

@discardableResult
    open func download(
        _ urlRequest: URLRequestConvertible,
        to destination: DownloadRequest.DownloadFileDestination? = nil)
        -> DownloadRequest
    {
        do {
            let urlRequest = try urlRequest.asURLRequest()
            return download(.request(urlRequest), to: destination)
        } catch {
            return download(nil, to: destination, failedWith: error)
        }
    }

發(fā)起下載請(qǐng)求

@discardableResult
    open func download(
        resumingWith resumeData: Data,
        to destination: DownloadRequest.DownloadFileDestination? = nil)
        -> DownloadRequest
    {
        return download(.resumeData(resumeData), to: destination)
    }

恢復(fù)下載,從某data開(kāi)始

 // MARK: Private - Download Implementation

    private func download(
        _ downloadable: DownloadRequest.Downloadable,
        to destination: DownloadRequest.DownloadFileDestination?)
        -> DownloadRequest
    {
        do {
            let task = try downloadable.task(session: session, adapter: adapter, queue: queue)
            let download = DownloadRequest(session: session, requestTask: .download(downloadable, task))

            download.downloadDelegate.destination = destination

            delegate[task] = download

            if startRequestsImmediately { download.resume() }

            return download
        } catch {
            return download(downloadable, to: destination, failedWith: error)
        }
    }

下載的實(shí)現(xiàn)調(diào)用

后面的update..也差不多都是這樣的方式,
總的來(lái)說(shuō)通過(guò)SessionManager提供的api我們可以很簡(jiǎn)單的發(fā)起一次請(qǐng)求,
再通過(guò)ResponseSerialization中對(duì)request的extension,我們可以調(diào)用如responseString\responseData等方法獲取到結(jié)果返回的回調(diào).
這樣一次完整的網(wǎng)絡(luò)請(qǐng)求基本就完成了,基礎(chǔ)的話,我們可以不用管每次請(qǐng)求,響應(yīng),管理等各種狀態(tài)的協(xié)議,我們基本不需要再去實(shí)現(xiàn),當(dāng)然有特殊需求的時(shí)候,我們只要在合適的地方實(shí)現(xiàn)協(xié)議即可
在我們使用時(shí)
我們僅需要如此即可

Alamofire.request("https://www.baidu.com").responseString { (response) in
            print(response.result.value ?? "")
        }

當(dāng)然我們也可以針對(duì)自己應(yīng)用中的環(huán)境在加一層封裝
例如我有一個(gè)項(xiàng)目

static let shared = XLXNetManager()
...
fileprivate class func get(url:URLConvertible, parameters : Parameters, success : @escaping (_ value : String)->(), failure : @escaping (_ error : Error)->()) {
        Alamofire
            .request(url, method: .get, parameters: appendAutoParametersDictionary(parameters), encoding: URLEncoding.default, headers: nil)
            .responseString { (response) in
                switch response.result {
                case .success(let value):
                    success(value)
                case .failure(let error):
                    XLXLog("error:\(error)")
                    failure(error)
            }
        }
    }
    
    fileprivate class func post(url:URLConvertible, parameters : Parameters, success : @escaping (_ value : String)->(), failure : @escaping (_ error : Error)->()) {
        Alamofire
            .request(url, method: .post, parameters: appendAutoParametersDictionary(parameters), encoding: URLEncoding.default, headers: nil)
            .responseString { (response) in
            switch response.result {
            case .success(let value):
                success(value)
            case .failure(let error):
                XLXLog("error:\(error)")
                failure(error)
            }
        }
    }
    
class func get_userHelp(success : @escaping (_ value : String)->(), failure : @escaping (_ error : Error)->()) {
        get(url: userHelpUrl, parameters: [:], success: success, failure: failure)
    }

這樣我在使用的時(shí)候

XLXNetManager.get_userHelp(success: {(ressult) in
            //利用如HandyJSON等三方或者系統(tǒng),進(jìn)行模型轉(zhuǎn)換....
            XLXLog(ressult)
        }) { (error) in
            XLXLog(error)
        }

完全的類似AFNetworking時(shí)候的使用方式....
當(dāng)然,這個(gè)是自由的.任你發(fā)揮

未完待續(xù),接下來(lái)還會(huì)簡(jiǎn)單介紹其他非關(guān)鍵的類..

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末敬矩,一起剝皮案震驚了整個(gè)濱河市概行,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌弧岳,老刑警劉巖凳忙,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件业踏,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡涧卵,警方通過(guò)查閱死者的電腦和手機(jī)勤家,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)柳恐,“玉大人伐脖,你說(shuō)我怎么就攤上這事±稚瑁” “怎么了讼庇?”我有些...
    開(kāi)封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)近尚。 經(jīng)常有香客問(wèn)我蠕啄,道長(zhǎng),這世上最難降的妖魔是什么戈锻? 我笑而不...
    開(kāi)封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任歼跟,我火速辦了婚禮,結(jié)果婚禮上格遭,老公的妹妹穿的比我還像新娘嘹承。我一直安慰自己,他們只是感情好如庭,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布叹卷。 她就那樣靜靜地躺著,像睡著了一般坪它。 火紅的嫁衣襯著肌膚如雪骤竹。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天往毡,我揣著相機(jī)與錄音蒙揣,去河邊找鬼。 笑死开瞭,一個(gè)胖子當(dāng)著我的面吹牛懒震,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播嗤详,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼个扰,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了葱色?” 一聲冷哼從身側(cè)響起递宅,我...
    開(kāi)封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后办龄,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體烘绽,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年俐填,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了安接。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡英融,死狀恐怖赫段,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情矢赁,我是刑警寧澤糯笙,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站撩银,受9級(jí)特大地震影響给涕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜额获,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一够庙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧抄邀,春花似錦耘眨、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至奥喻,卻和暖如春偶宫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背环鲤。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工纯趋, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人冷离。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓吵冒,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親西剥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子痹栖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容