Swift HTTP加載請求Loading Requests教程
正文
到目前為止,我們已經(jīng)看了一個簡單的Request/Response的結(jié)構(gòu)體和實(shí)現(xiàn)。接下來,我們來討論一下發(fā)送請求和接收響應(yīng)。
如果我們回想一下第一節(jié),我們會用HTTP回調(diào)給他,我們發(fā)送了一個請求,并且最終得到了一個響應(yīng)(忽略Error)沒有任何“任務(wù)”或者代理亦或其他什么東西。我們發(fā)送(或加載)一個請求,最終都會得到一個響應(yīng)。
如果我們用一個方法來描述那個功能,那么這個方法如下所示
func load(request: HTTPRequest, completion: @escaping (HTTPResponse) -> Void)
我們發(fā)送了一個請求,在未來的某個節(jié)點(diǎn),閉包將會被執(zhí)行,表里涵蓋的是我們要的響應(yīng)。當(dāng)然,單個方法并不是我們想要的;換句話說,我們想要表述的是一個接口。所以,我們會把他封裝進(jìn)一個protocol中:
public protocol HTTPLoading {
func load(request: HTTPRequest, completion: @escaping (HTTPResponse) -> Void)
}
當(dāng)然,我們可能會得到一個error響應(yīng)。所以,我們要用自己的“results”重命名(typealiase)來替換HTTPResponse:
func load(request: HTTPRequest, completion: @escaping (HTTPResult) -> Void)
讓我們停下來欣賞一下。 這個方法就是我們定義網(wǎng)絡(luò)框架的核心功能所需要的全部。 就是這樣:一個方法。 棒極了。
遵循HTTPLoading協(xié)議
URLSession就像“路上的橡膠”,這是在我們的請求通過空中(或有線)發(fā)送到我們指定的服務(wù)器之前需要清除的最后一個障礙。 因此,我們在 URLSession 上實(shí)現(xiàn) HTTPLoading 是為了將 HTTPRequest 轉(zhuǎn)換為會話需要的 URLRequest,這是有道理的:
extension URLSession: HTTPLoading {
public func load(request: HTTPRequest, completion: @escaping (HTTPResult) -> Void)
guard let url = request.url else {
// we couldn't construct a proper URL out of the request's URLComponents
completion(.failure(...))
return
}
// construct the URLRequest
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = request.method.rawValue
// copy over any custom HTTP headers
for (header, value) in request.headers {
urlRequest.addValue(value, forHTTPHeaderField: header)
}
if request.body.isEmpty == false {
// if our body defines additional headers, add them
for (header, value) in request.body.additionalHeaders {
urlRequest.addValue(value, forHTTPHeaderField: header)
}
// attempt to retrieve the body data
do {
urlRequest.httpBody = try request.body.encode()
} catch {
// something went wrong creating the body; stop and report back
completion(.failure(...))
return
}
}
let dataTask = session.dataTask(with: urlRequest) { (data, response, error) in
// construct a Result<HTTPResponse, HTTPError> out of the triplet of data, url response, and url error
let result = HTTPResult(request: request, responseData: data, response: response, error: error)
completion(result)
}
// off we go!
dataTask.resume()
}
}
這應(yīng)該很容易理解。 我們正在執(zhí)行從 HTTPRequest 值中提取信息并將其應(yīng)用于 URLRequest 的步驟。 如果在任何時候出現(xiàn)問題,那么我們將報(bào)告錯誤。 (您需要自己填寫 ... 部分以構(gòu)造適當(dāng)?shù)?HTTPError 值)
假設(shè)構(gòu)建一切順利,我們最終會得到一個 URLRequest,我們可以將其轉(zhuǎn)換為 URLSessionDataTask 并執(zhí)行它。 當(dāng)它完成時,我們將獲取響應(yīng)值,將它們轉(zhuǎn)換為 HTTPResult,并通過完成塊報(bào)告回來。
創(chuàng)建Result
在傳輸過程中的任何時候,我們的請求都可能失敗。 如果我們處于飛行模式或其他“未連接”狀態(tài),則請求可能永遠(yuǎn)不會發(fā)送。 如果我們正在發(fā)送請求(在我們得到響應(yīng)之前),網(wǎng)絡(luò)連接可能會斷開。 或者它可能會在我們發(fā)送后但在我們收到回復(fù)之前掉線。 或者它可能會在我們開始收到響應(yīng)之后但在完全接收到響應(yīng)之前下降。
這就是我們在定義請求和響應(yīng)類型時創(chuàng)建 HTTPError 結(jié)構(gòu)的原因,這意味著我們需要更加努力地構(gòu)建我們的結(jié)果,而不是簡單地檢查“我是否得到了一些數(shù)據(jù)”。
在高層次上,初始化 HTTPResult 的邏輯大致如下所示:
var httpResponse: HTTPResponse?
if let r = response as? HTTPURLResponse {
httpResponse = HTTPResponse(request: request, response: r, body: responseData ?? Data())
}
if let e = error as? URLError {
let code: HTTPError.Code
switch e.code {
case .badURL: code = .invalidRequest
case .unsupportedURL: code = ...
case .cannotFindHost: code = ...
...
default: code = .unknown
}
self = .failure(HTTPError(code: code, request: request, response: httpResponse, underlyingError: e))
} else if let someError = error {
// an error, but not a URL error
self = .failure(HTTPError(code: .unknown, request: request, response: httpResponse, underlyingError: someError))
} else if let r = httpResponse {
// not an error, and an HTTPURLResponse
self = .success(r)
} else {
// not an error, but also not an HTTPURLResponse
self = .failure(HTTPError(code: .invalidResponse, request: request, response: nil, underlyingError: error))
}
用法
HTTPLoading 使用方法:
public class StarWarsAPI {
private let loader: HTTPLoading = URLSession.shared
public func requestPeople(completion: @escaping (...) -> Void) {
var r = HTTPRequest()
r.host = "swapi.dev"
r.path = "/api/people"
loader.load(request: r) { result in
// TODO: interpret the result
completion(...)
}
}
}
我想在這里指出,我們在任何時候都不會解釋Response的狀態(tài)代碼。 獲得 500 Internal Server Error 或 404 Not Found 響應(yīng)是成功的響應(yīng)。 在這一層,“成功”意味著“我們得到了回應(yīng)”,而不是“回應(yīng)表明某種語義錯誤”。 解釋狀態(tài)代碼是特定于應(yīng)用程序的邏輯。 在未來的帖子中,我們將允許基于狀態(tài)代碼的可定制的、特定于應(yīng)用程序的行為(例如跟隨重定向或重試請求)。
我們定義的這個單一方法看似簡單,但也不完整。 我們還沒有指出任何主動取消請求的方法,我們需要調(diào)整我們的 HTTPLoading 協(xié)議以添加更多功能。 我們還將把它從協(xié)議轉(zhuǎn)換為類,原因我將在以后的帖子中解釋。
盡管存在這些小漏洞,該協(xié)議在其簡單性方面仍然很漂亮,它展示了一個好的問題概念化如何能夠產(chǎn)生強(qiáng)大而美麗的東西。
簡單是最終的復(fù)雜。
在下一篇文章中,我們將研究使用 HTTPLoading 協(xié)議來簡化單元測試。
以上就是Swift HTTP加載請求Loading Requests教程的詳細(xì)內(nèi)容,更多關(guān)于Swift HTTP加載請求的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
在 Swift 中測試 UIAlertController的方法
這篇文章主要介紹了在 Swift 中測試 UIAlertController的方法的,需要的朋友可以參考下2015-10-10
深入解析Swift中switch語句對case的數(shù)據(jù)類型匹配的支持
這篇文章主要介紹了Swift中switch語句對case的數(shù)據(jù)類型匹配的支持,Swift中switch...case語句支持多種數(shù)據(jù)類型的匹配判斷,十分強(qiáng)大,需要的朋友可以參考下2016-04-04
Swift 中如何使用 Option Pattern 改善可選項(xiàng)的 API 設(shè)計(jì)
這篇文章主要介紹了Swift 中如何使用 Option Pattern 改善可選項(xiàng)的 API 設(shè)計(jì),幫助大家更好的進(jìn)行ios開發(fā),感興趣的朋友可以了解下2020-10-10
Swift項(xiàng)目中利用SWRevealViewController實(shí)現(xiàn)側(cè)滑菜單
這篇文章主要介紹了Swift項(xiàng)目中利用SWRevealViewController實(shí)現(xiàn)側(cè)滑菜單,需要的朋友可以參考下2015-12-12
swift4 使用DrawerController實(shí)現(xiàn)側(cè)滑菜單功能的示例代碼
這篇文章主要介紹了swift4 使用DrawerController實(shí)現(xiàn)側(cè)滑功能的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-06-06
淺談Swift編程中switch與fallthrough語句的使用
這篇文章主要介紹了Swift編程中switch與fallthrough語句的使用,用于基本的流程控制,需要的朋友可以參考下2015-11-11

