關於 Swift 中的 URLSession 和 Alamofire 源碼閱讀

URLSession/NSURLSession

URLSession至關於管理一個請求的類,它涉及到URL/URLRequest/URLSessionConfiguration/URLSessionTask。經過這幾個類的綜合使用,咱們就能夠很方便的建立請求javascript

建立GET請求

let userTel = "13231852031"
let userPassword = "123456"
let defaultConfiguration = URLSessionConfiguration.default
let sessionWithoutADelegate = URLSession(configuration: defaultConfiguration)
if let url = URL(string: "http://127.0.0.1:8080/v1/login?usertel=\(userTel)&userpassword=\(userPassword)") {
    (sessionWithoutADelegate.dataTask(with: url) { (data, response, error) in
        if let error = error {
            print("Error: \(error)")
        } else if let response = response,
            let data = data,
            let string = String(data: data, encoding: .utf8) {
            print("Response: \(response)")
            print("DATA:\n\(string)\nEND DATA\n")
        }
    }).resume()
}複製代碼

建立POST請求

let defaultConfiguration = URLSessionConfiguration.default
let sessionWithoutADelegate = URLSession(configuration: defaultConfiguration)
let paramer: [String: String] = ["userTel":"13231852031","userPassword":"123456"]

if let url = URL(string: "http://127.0.0.1:8080/v1/register") {
    var request = URLRequest.init(url: url)
    request.httpMethod = "POST"
    request.httpBody = try? JSONSerialization.data(withJSONObject: paramer, options: JSONSerialization.WritingOptions.prettyPrinted)

    sessionWithoutADelegate.dataTask(with: request, completionHandler: { (data, response, error) in
        if let error = error {
            print("Error:\(error)")
        }else if let response = response,
            let data = data,
            let string = String(data: data,encoding: .utf8){
            print("Response: \(response)\n")
            print("DATA:\(string)\n")
        }
    }).resume()
}複製代碼

根據Alamofire的README讀其源碼

Swift版本和Alamofire版本

  • Swift:3.0
  • Alamofire:4.4.0

Making a Request

Alamofire.request("https://httpbin.org/get")複製代碼

首先,文檔中給出上面的一個例子,咱們能夠看到很簡單就發送了一個請求。接下來咱們能夠看一下Alamofire是如何實現的request,經過跳轉咱們能夠看到該方法是實如今Alamofire.swift文件中:html

@discardableResult
public func request(
    _ url: URLConvertible,
    method: HTTPMethod = .get,
    parameters: Parameters? = nil,
    encoding: ParameterEncoding = URLEncoding.default,
    headers: HTTPHeaders? = nil)
    -> DataRequest
{
    return SessionManager.default.request(
        url,
        method: method,
        parameters: parameters,
        encoding: encoding,
        headers: headers
    )
}複製代碼

首先該方法經過URLConvertible協議來判斷URL的有效性(如未無效URL會拋出Error),而後方法默認爲GET,parameters參數默認爲空,參數編碼默認爲URLEncoding.default,header默認爲空。在該方法中又調用了SessionManager,接下來咱們再跳轉到SessionManager,在SessionManager中的request方法實現以下(因爲方法較長下面代碼只貼關鍵部分):java

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)
}複製代碼

在上述實現中咱們能夠看到,若是各項參數都沒問題的話,它會進入do代碼塊中request方法.
do中的request方法實現以下:ios

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)
}複製代碼

仍是在判斷各項參數都正確後調用resume方法,在該方法中它會調用URLSessionTask的resume()來啓動該次請求。由此咱們能夠看出雖然咱們只是調用這麼簡單的一句話,可是裏面仍是由不少條件和邏輯的,只不過這一切Alamofire替咱們作了。git

上述的代碼邏輯若是把全部的錯誤檢查和判斷邏輯簡化掉,相似於下方的代碼:github

let url = URL.init(string: "http://example.com")
let session = URLSession.shared
session.dataTask(with: url!).resume()複製代碼

Response Handling

Alamofire默認有五種response handler:編程

  • Response Handler - Unserialized Response(沒有序列化response)
  • Response Data Handler - Serialized into Data(將數據序列化爲Data)
  • Response String Handler - Serialized into String(將數據序列化爲String)
  • Response JSON Handler - Serialized into Any(將數據序列化爲Any)
  • Response PropertyList (plist) Handler - Serialized into Any(同上)

Tip:上述五種response handler都沒對返回數據的HTTPURLResponse作驗證,Alamofire是經過Response Validation作驗證的。json

Response Handler
Alamofire.request("https://httpbin.org/get").response { response in
}複製代碼

ResponseSerialization.swift中的方法實現:swift

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)
    }複製代碼

DefaultDataResponse爲結構體,包含request、response、data等數據。api

Alamofire不建議使用這種response serializers。

Response Data Handler
Alamofire.request("https://httpbin.org/get").responseData { response in
}複製代碼

若是請求數據和解析數據的過程當中沒有發生錯誤,它將返回一個Data類型的數據,Response的Result將會是.success。在該方法的實現源碼中調用了responseSerializer: DataRequest.dataResponseSerializer()序列化數據的方法

Response String Handler
Alamofire.request("https://httpbin.org/get").responseString { response in
}複製代碼

該Handler經過responseStringSerializer將返回的Data數據轉爲String類型,若是沒有錯誤發生,而且服務器數據成功轉爲String,response的Result將爲.success而且值爲String。

Response JSON Handle
Alamofire.request("https://httpbin.org/get").responseJSON { response in
}複製代碼

該handler經過調用jsonResponseSerializer方法來轉換數據類型,若沒發生錯誤request同上

Chained Response Handlers

Tip:使用鏈式response包含幾個handler,就會請求幾回數據數據

Response Handler Queue

Response handlers默認在主線程隊列中執行,可是咱們能夠提供一個自定義線程隊列

Response Validation

默認狀況下,Alamofire認爲任何已完成的請求都是成功的,不管相應內容是什麼。你能夠在handler以前調用validate來判斷status code

HTTP Methods

Alamofire支持HTTP的GET、POST、DELETE等方法,並將這些方法聲明爲HTTPMethod的enum

Parameter Encoding

Alamofire提供三種參數編碼方式URL,JSON和PropertyList

Session Manager

SessionManager.swift是Alamofire中比較重要的一個文件,它是URLSession的管理類,文件中是對request方法的具體實現。正是有該類才使得Alamofire發起一個請求很是簡單。
經過源碼咱們能夠看見若是不設置URLSessionConfiguration,默認爲URLSessionConfiguration.default
你也能夠將URLSessionConfiguration修改成Background Configuration和Ephemeral Configuration

Request

Requests能夠被暫停(suspended)復位(resumed)和取消(cancelled)

Routing Requests

隨着APP的不斷更新和迭代,構建網絡層的通用模式很重要。如何路由你的request也是很是重要的一個部分。URLConvertibleURLRequestConvertible協議能夠幫助你構建路由的通用部分

URLConvertible

String, URL, 和URLComponents是默認遵照URLConvertible協議的,因此你能夠在request中寫String, URL, 和URLComponents,它會自動檢測request中的參數,根據參數類型找到相應的方法轉爲URL。你也能夠根據文檔中的例子來構造本身的路由

URLRequestConvertible

URLRequest默認遵照該協議,該協議用來構造URLRequest

代碼中的關鍵字解釋

  • @discardableResult:此關鍵字表示修飾的方法沒必要接受返回值Swift 3.0 中方法的返回值必須有接收不然會報警告,固然其實主要目的是爲了不開發人員忘記接收返回值的狀況,可是有些狀況下確實不須要使用返回值可使用"_"接收來忽略返回值。固然你也能夠增長@discardableResult聲明,告訴編譯器此方法能夠不用接收返回值
  • open:open 一個元素在其餘module中仍是能夠被override
  • static:在方法的func關鍵字以前加上關鍵字static或者class均可以用於指定類方法.不一樣的是用class關鍵字指定的類方法能夠被子類重寫
  • @escaping

總結

至此,Alamofire的源碼已經瞭解完,我在這裏只是簡單的瞭解一下Alamofire是如何實現最基本的GET和POST請求如何實現。能夠看到,它經過幾層包裝實現了參數的校驗、request支持String、URL和URLComponents三種方式。因爲水平有限,對於它下載上傳等功能的源碼並無十分理解,因此在這並無寫,你們有能力的能夠自行查看源碼。在Alamofire中比較重要的幾個文件Alamofire.swift/Response.swift/SessionManager.swift/Request.swift。

github地址

參考

相關文章
相關標籤/搜索