Alamofire(3)— Request

😊😊😊Alamofire專題目錄,歡迎及時反饋交流 😊😊😊前端


Alamofire 目錄直通車 --- 和諧學習,不急不躁!swift


上一篇 Alamofire-後臺下載 其中就介紹了關於 SesssionManagerSessionDelegate的分層!下面我在總結一下,而後開始一個 Alamofire 很是重要的模塊 Request!數組

1、SessionManager的總結

  • SesssionManager 就是對外提供的管理者,這個管理者具有整個 Alamofire 的全部功能。安全

    • request 請求、download、upload、stream
    • 請求頭信息設置以及多表單頭信息設置
    • session 直接對外提供,方便自由處理
    • 初始化暴露,方便自由工廠構造,好比 URLSessionConfiguration 的配置模式
    • 重試以及適配請求
    • 閉包對外提供:backgroundCompletionHandler 後臺下載回來監聽閉包
    • 其中一個很是重要的點就是 SesssionManagerSession 的代理移交給了一個專門的類 : SessionDelegate
  • SessionDelegate實現了 URLSessionDelegateURLSessionTaskDelegateURLSessionDataDelegateURLSessionDownloadDelegateURLSessionStreamDelegate 等代理bash

  • 更多有意思的是:SessionDelegate還提供了對外的閉包,意味着全部的內部實現的代理狀況,再外界均可以進行監聽。固然這個全部的對外閉包分爲兩種狀況:markdown

    • 在原來代理回調的內部添加閉包執行。

  • 另外一種是二選一,若是代理回來就不執行下層代理下發,執行對外閉包回調

總結:SesssionManager負責建立和管理 Request 對象及其底層NSURLSession網絡

首先給你們貼出一張很是熟悉的圖,看懂了這張圖對下面理解 Request 的幫助大大的session

  • 從上面這張圖能夠看出,咱們的對外模塊是SesssionManager,他給外界的用戶提供了不少的功能
  • 可是這些工做的真正實現者是由iOS、Android、前端、後臺、測試實現的!
  • 其中單拿 iOS 模塊的任務來講,有 首頁、發現、個人、SDK、視頻....模塊要實現,可是咱們的項目經理有可能都不知道這些究竟是什麼,怎麼實現!全部來講若是所有交給SesssionManager來實現,顯然耦合性過強,還有任務亂七八糟,沒有體現一個牛逼項目分層架構的效果。因此在 iOS 任務細化和SesssionManager 之間就缺了一個小管理者,對下:他知道具體事務和調度。對上:他能和SesssionManager協調配合。那就是 Request

2、Request

1. Request參數編碼

  • 首先說明一下 Alamofire 支持編碼格式,具體格式差別根據名字很容易理解,實在不能理解的能夠自行百度查漏補缺閉包

    • URLEncoding
    • JSONEncoding
    • PropertyListEncoding
  • let encodedURLRequest = try encoding.encode(originalRequest!, with: parameters) 經過這句代碼還編碼請求架構

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

    guard let parameters = parameters else { return urlRequest }

    if let method = HTTPMethod(rawValue: urlRequest.httpMethod ?? "GET"), encodesParametersInURL(with: method) {
        guard let url = urlRequest.url else {
            throw AFError.parameterEncodingFailed(reason: .missingURL)
        }

        if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), !parameters.isEmpty {
            let percentEncodedQuery = (urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters)
            urlComponents.percentEncodedQuery = percentEncodedQuery
            urlRequest.url = urlComponents.url
        }
    } else {
        if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
            urlRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
        }
        urlRequest.httpBody = query(parameters).data(using: .utf8, allowLossyConversion: false)
    }
    return urlRequest
}
複製代碼
  • 代碼自行查閱,這裏總結一下

  • 首先取出請求方法,根據不一樣的請求方法,參數編碼是不一樣的

    • .get, .head, .delete 這三個方法是把參數直接拼接到 URL 後面
    • 其餘經過請求體 (httpBody) 的形式編碼
  • 由於咱們的請求是經過 ASCII編碼 的,因此要進行百分號編碼,第一步就是對當前請求的全部路由百分號編碼

  • 參數便利編碼, 拼接拿出

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: "&")
}
複製代碼
  • 經過 ASCII 有小到大進行排序
  • queryComponents 這個方法代碼過分省略。
    • 裏面進行遞歸參數,
    • key和value 取出,而後進行了百分號編碼。
    • 放進元組保存,造成參數對
  • 外面講元組加入數組中
  • map 映射 ($0)=\($1)
  • 元素之間鍵入一個分隔符號 &

普通方法就直接拼接到URL的後面,例如POST方法就是把這些編碼好的參數對放入請求體中。其中還要加入Content-Type的請求頭

2. Request內部關係梳理

探索完 request 繁雜事務之一的參數編碼以後,開始分析內部:url -> request -> task 的過程。這個過程當中還創建 task以及request 之間的綁定關係

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)
    }
}
複製代碼
  • 內部建立 Requestable 來幫助下層的 DataRequest 建立 Task,也是常規說法,任務分層,架構思路更清晰
  • 綁定 task 和 request , 方便在 SessionDelegate 下發任務,task 直接檢索,request 方便直接獲取
  • 直接任務 request.resume() 啓動

  • Request 初始化的時候利用了枚舉的便利性,構造建立,相關信息保存

可能不少小夥伴這個時候就會有疑慮,你不就是經過SessionDelegate實現代理, 爲何這裏還要有一個 DataTaskDelegate

  • 首先必定要明白 SessionDelegate 是全部 Session 的代理遵循者,任何的代理都會來到這裏響應。

  • DataTaskDelegate 是咱們具體繁瑣任務實現者,這裏面全部方法都是由 SessionDelegate 下發響應的。

  • 說白了就是總體與局部的關係

  • SessionDelegate 是事件總響應者,可是具體的事務應該交給具體的人去執行,不能由於在 SessionDelegate 可以拿到全部的響應,那麼全部的代碼都羅列在這裏,很顯然若是那樣作必然會致使混亂,咱們根據不一樣的需求,響應總代理而後根據需求的不一樣交給專業的人去作專業的事。耦合性大大下降,架構的分層更加明顯。

  • 其中還有異步很是重要的點:delegate[task] = request 給咱們的SessionDelegate 提供一個方便及時得到能力

open func urlSession( _ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL)
{
  // 省略屬性閉包回調的狀況
    if let delegate = self[downloadTask]?.delegate as? DownloadTaskDelegate {
        delegate.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location)
    }
}
複製代碼
  • 這段代碼典型的經過咱們前面創建的綁定關係,經過每次代理回調的 task 找到相應的 Request 而後由於屬性關係,直接拿出 Request 的屬性delegate 來處理相關事務
func urlSession(
    _ session: URLSession,
    downloadTask: URLSessionDownloadTask,
    didFinishDownloadingTo location: URL)
{
    temporaryURL = location

    guard
        let destination = destination,
        let response = downloadTask.response as? HTTPURLResponse
    else { return }

    let result = destination(location, response)
    let destinationURL = result.destinationURL
    let options = result.options

    self.destinationURL = destinationURL

    do {
        if options.contains(.removePreviousFile), FileManager.default.fileExists(atPath: destinationURL.path) {
            try FileManager.default.removeItem(at: destinationURL)
        }

        if options.contains(.createIntermediateDirectories) {
            let directory = destinationURL.deletingLastPathComponent()
            try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
        }
        try FileManager.default.moveItem(at: location, to: destinationURL)
    } catch {
        self.error = error
    }
}
複製代碼
  • 文件下載完畢轉移存儲地址,文件處理相關 以及error狀況
  • 上面就是最典型的繁重噁心操做,必然是不須要被SessionDelegate 所須要知道的!

咱們從 SessionManager -> Request 這一過程明面上就是直接交付,可是背地都是經過代理響應聯通的:SessionDelegate -> 具體的Request的delegate。看到這裏,是否是感受很是的爽!優秀的框架老是可以給你在思想方面帶來很大的提高,咱們平時開發的時候也會有很噁心的耦合度,通信問題。那麼想必 Alamofire 的設計思路確定可以給你帶來必定的啓示!

就問此時此刻還有誰?45度仰望天空,該死!我這無處安放的魅力!

相關文章
相關標籤/搜索