URLSession/NSURLSession
URLSession相當於管理一個請求的類,它涉及到URL/URLRequest/URLSessionConfiguration/URLSessionTask。通過這幾個類的綜合使用,我們就可以很方便的建立請求
建立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檔案中:
@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方法實現如下(由於方法較長下面程式碼只貼關鍵部分):
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方法實現如下:
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替我們做了。
上述的程式碼邏輯如果把所有的錯誤檢查和判斷邏輯簡化掉,類似於下方的程式碼:
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做驗證的。
Response Handler
Alamofire.request("https://httpbin.org/get").response { response in
}複製程式碼
ResponseSerialization.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等資料。
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也是非常重要的一個部分。URLConvertible
和URLRequestConvertible
協議可以幫助你構建路由的通用部分
URLConvertible
String, URL, 和URLComponents是預設遵守URLConvertible
協議的,所以你可以在request中寫String, URL, 和URLComponents,它會自動檢測request中的引數,根據引數型別找到相應的方法轉為URL。你也可以根據文件中的例子來構造自己的路由
URLRequestConvertible
URLRequest預設遵守該協議,該協議用來構造URLRequest
程式碼中的關鍵字解釋
@discardableResult
:此關鍵字表示修飾的方法不必接受返回值Swift 3.0 中方法的返回值必須有接收否則會報警告,當然其實主要目的是為了避免開發人員忘記接收返回值的情況,但是有些情況下確實不需要使用返回值可以使用"_"接收來忽略返回值。當然你也可以增加@discardableResult宣告,告訴編譯器此方法可以不用接收返回值open
:open 一個元素在其他module中還是可以被overridestatic
:在方法的func關鍵字之前加上關鍵字static或者class都可以用於指定類方法.不同的是用class關鍵字指定的類方法可以被子類重寫@escaping
總結
至此,Alamofire的原始碼已經瞭解完,我在這裡只是簡單的瞭解一下Alamofire是如何實現最基本的GET和POST請求如何實現。可以看到,它通過幾層包裝實現了引數的校驗、request支援String、URL和URLComponents三種方式。由於水平有限,對於它下載上傳等功能的原始碼並沒有十分理解,所以在這並沒有寫,大家有能力的可以自行檢視原始碼。在Alamofire中比較重要的幾個檔案Alamofire.swift/Response.swift/SessionManager.swift/Request.swift。