這篇文章是 Alamofire 5.0 以前的文檔,最新文檔請(qǐng)查看: Alamofire 5 的使用 - 基本用法
對(duì)于使用Objective-C的開(kāi)發(fā)者,一定非常熟悉AFNetworking
這個(gè)網(wǎng)絡(luò)框架。在蘋(píng)果推出的Swift之后,AFNetworking
的作者專門(mén)用Swift來(lái)編寫(xiě)一個(gè)類似AFNetworking
的網(wǎng)絡(luò)框架,稱為Alamofire
。Alamofire地址 >>
我分兩篇文章介紹如何使用Alamofire框架。文章的內(nèi)容主要是翻譯Alamofire的readme。第二篇文章 >>
功能
- 鏈?zhǔn)秸?qǐng)求 / 響應(yīng)方法
- URL / JSON / plist參數(shù)編碼
- 上傳文件 / 數(shù)據(jù) / 流 / 多表單數(shù)據(jù)
- 使用請(qǐng)求或者斷點(diǎn)下載來(lái)下載文件
- 使用URL憑據(jù)進(jìn)行身份認(rèn)證
- HTTP響應(yīng)驗(yàn)證
- 包含進(jìn)度的上傳和下載閉包
- cURL命令的輸出
- 動(dòng)態(tài)適配和重試請(qǐng)求
- TLS證書(shū)和Public Key Pinning
- 網(wǎng)絡(luò)可達(dá)性
- 全面的單元和集成測(cè)試覆蓋率
組件庫(kù)
為了讓Alamofire
專注于核心網(wǎng)絡(luò)的實(shí)現(xiàn),Alamofire生態(tài)系統(tǒng)還有另外兩個(gè)庫(kù):
-
AlamofireImage:一個(gè)圖片庫(kù),包括圖像響應(yīng)序列化器、
UIImage
和UIImageView
的擴(kuò)展、自定義圖像濾鏡、內(nèi)存中自動(dòng)清除和基于優(yōu)先級(jí)的圖像下載系統(tǒng)。 - AlamofireNetworkActivityIndicator:控制iOS應(yīng)用的網(wǎng)絡(luò)活動(dòng)指示器。包含可配置的延遲計(jì)時(shí)器來(lái)幫助減少閃光,并且支持不受Alamofire管理的URLSession實(shí)例。
要求的使用環(huán)境
- iOS 10.0+ / macOS 10.12+ / tvOS 10.0+ / watchOS 3.0+
- Xcode 10.2+
- Swift 5+
安裝方法
CocoaPods
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '10.0'
use_frameworks!
target '項(xiàng)目名稱' do
pod 'Alamofire', '~> 5.0.0-beta.5'
end
iOS版本和Alamofire
版本可以自己根據(jù)實(shí)際情況自行更改。CocoaPods是比較常用的第三方庫(kù)管理工具,其他方法就不詳細(xì)說(shuō)了。
如何使用
發(fā)請(qǐng)求
Alamofire.request("http://baidu.com/")
響應(yīng)處理
直接在請(qǐng)求后面用點(diǎn)語(yǔ)法鏈接響應(yīng)處理:
Alamofire.request("https://httpbin.org/get").responseJSON { response in
print(response.request) // 原始的URL請(qǐng)求
print(response.response) // HTTP URL響應(yīng)
print(response.data) // 服務(wù)器返回的數(shù)據(jù)
print(response.result) // 響應(yīng)序列化結(jié)果,在這個(gè)閉包里,存儲(chǔ)的是JSON數(shù)據(jù)
if let JSON = response.result.value {
print("JSON: \(JSON)")
}
}
在上面的例子中,responseJSON
handler直接拼接到請(qǐng)求后面,當(dāng)請(qǐng)求完成后被調(diào)用。這個(gè)閉包一旦收到響應(yīng)后,就會(huì)處理這個(gè)響應(yīng),并不會(huì)因?yàn)榈却?wù)器的響應(yīng)而造成阻塞執(zhí)行。請(qǐng)求的結(jié)果僅在響應(yīng)閉包的范圍內(nèi)可用。其他任何與服務(wù)器返回的響應(yīng)或者數(shù)據(jù)相關(guān)的操作,都必須在這個(gè)閉包內(nèi)執(zhí)行。
Alamofire默認(rèn)情況下包含五種不同的響應(yīng)handler:
// 響應(yīng) Handler - 未序列化的響應(yīng)
func response(
queue: DispatchQueue?,
completionHandler: @escaping (DefaultDataResponse) -> Void)
-> Self
// 響應(yīng)數(shù)據(jù) Handler - 序列化成數(shù)據(jù)類型
func responseData(
queue: DispatchQueue?,
completionHandler: @escaping (DataResponse<Data>) -> Void)
-> Self
// 響應(yīng)字符串 Handler - 序列化成字符串類型
func responseString(
queue: DispatchQueue?,
encoding: String.Encoding?,
completionHandler: @escaping (DataResponse<String>) -> Void)
-> Self
// 響應(yīng) JSON Handler - 序列化成Any類型
func responseJSON(
queue: DispatchQueue?,
completionHandler: @escaping (DataResponse<Any>) -> Void)
-> Self
// 響應(yīng) PropertyList (plist) Handler - 序列化成Any類型
func responsePropertyList(
queue: DispatchQueue?,
completionHandler: @escaping (DataResponse<Any>) -> Void))
-> Self
所有的響應(yīng)handler都不會(huì)對(duì)響應(yīng)進(jìn)行驗(yàn)證。也就是說(shuō)響應(yīng)狀態(tài)碼在400..<500
和500..<600
范圍內(nèi),都不會(huì)觸發(fā)錯(cuò)誤。
響應(yīng) Handler
response
handler不處理任何響應(yīng)數(shù)據(jù)。它僅僅是從URL session delegate中轉(zhuǎn)發(fā)信息。
Alamofire.request("https://httpbin.org/get").response { response in
print("Request: \(response.request)")
print("Response: \(response.response)")
print("Error: \(response.error)")
if let data = response.data, let utf8Text = String(data: data, encoding: .utf8) {
print("Data: \(utf8Text)")
}
}
一般情況下不建議使用這種沒(méi)有響應(yīng)序列化器的handler,而應(yīng)該使用下面有特定序列化器的handler。
響應(yīng)數(shù)據(jù) Handler
responseData
handler使用responseDataSerializer
(這個(gè)對(duì)象把服務(wù)器的數(shù)據(jù)序列化成其他類型)來(lái)提取服務(wù)器返回的數(shù)據(jù)。如果沒(méi)有返回錯(cuò)誤并且有數(shù)據(jù)返回,那么響應(yīng)Result
將會(huì)是.success
,value
是Data
類型。
Alamofire.request("https://httpbin.org/get").responseData { response in
debugPrint("All Response Info: \(response)")
if let data = response.result.value, let utf8Text = String(data: data, encoding: .utf8) {
print("Data: \(utf8Text)")
}
}
響應(yīng)字符串 Handler
responseString
handler使用responseStringSerializer
對(duì)象根據(jù)指定的編碼格式把服務(wù)器返回的數(shù)據(jù)轉(zhuǎn)換成String
。如果沒(méi)有返回錯(cuò)誤并且服務(wù)器的數(shù)據(jù)成功地轉(zhuǎn)換為String
,那么響應(yīng)Result
將會(huì)是.success
,value
是String
類型。
Alamofire.request("https://httpbin.org/get").responseString { response in
print("Success: \(response.result.isSuccess)")
print("Response String: \(response.result.value)")
}
如果沒(méi)有指定編碼格式,將會(huì)使用服務(wù)器的HTTPURLResponse
指定的格式。如果服務(wù)器無(wú)法確定編碼格式,那么默認(rèn)使用.isoLatin1
。
響應(yīng) JSON Handler
responseJSON
handler使用responseJSONSerializer
根據(jù)指定的JSONSerialization.ReadingOptions
把服務(wù)器返回的數(shù)據(jù)轉(zhuǎn)換成Any
類型。如果沒(méi)有返回錯(cuò)誤并且服務(wù)器的數(shù)據(jù)成功地轉(zhuǎn)換為JSON
對(duì)象,那么響應(yīng)Result
將會(huì)是.success
,value
是Any
類型。
Alamofire.request("https://httpbin.org/get").responseJSON { response in
debugPrint(response)
if let json = response.result.value {
print("JSON: \(json)")
}
}
所有JSON的序列化,都是使用JSONSerialization
完成的。
鏈?zhǔn)巾憫?yīng)handler
響應(yīng)handler可以鏈接在一起:
Alamofire.request("https://httpbin.org/get")
.responseString { response in
print("Response String: \(response.result.value)")
}
.responseJSON { response in
print("Response JSON: \(response.result.value)")
}
注意:在同一個(gè)請(qǐng)求中使用多個(gè)響應(yīng)handler,要求服務(wù)器的數(shù)據(jù)會(huì)被序列化多次,每次對(duì)應(yīng)一個(gè)handler。
響應(yīng)handler隊(duì)列
默認(rèn)情況下,響應(yīng)handler是在主隊(duì)列執(zhí)行的。但是我們也可以自定義隊(duì)列:
let utilityQueue = DispatchQueue.global(qos: .utility)
Alamofire.request("https://httpbin.org/get").responseJSON(queue: utilityQueue) { response in
print("Executing response handler on utility queue")
}
響應(yīng)驗(yàn)證
默認(rèn)情況下,Alamofire把所有完成的請(qǐng)求當(dāng)做是成功的請(qǐng)求,無(wú)論響應(yīng)的內(nèi)容是什么。如果響應(yīng)有一個(gè)不能被接受的狀態(tài)碼或者M(jìn)IME類型,在響應(yīng)handler之前調(diào)用validate
將會(huì)產(chǎn)生錯(cuò)誤。
手動(dòng)驗(yàn)證
Alamofire.request("https://httpbin.org/get")
.validate(statusCode: 200..<300)
.validate(contentType: ["application/json"])
.responseData { response in
switch response.result {
case .success:
print("Validation Successful")
case .failure(let error):
print(error)
}
}
自動(dòng)驗(yàn)證
自動(dòng)驗(yàn)證在200…299
范圍內(nèi)的狀態(tài)碼;如果請(qǐng)求頭中有指定Accept
,那么也會(huì)驗(yàn)證響應(yīng)頭的與請(qǐng)求頭Accept
一樣的Content-Type
。
Alamofire.request("https://httpbin.org/get").validate().responseJSON { response in
switch response.result {
case .success:
print("Validation Successful")
case .failure(let error):
print(error)
}
}
響應(yīng)緩存
響應(yīng)緩存是使用系統(tǒng)的框架URLCache
來(lái)處理的。它提供了內(nèi)存和磁盤(pán)上的緩存,并允許我們控制內(nèi)存和磁盤(pán)的大小。
默認(rèn)情況下,Alamofire
利用共享的URLCache
。
HTTP方法
HTTPMethod
列舉了下面的這些方法:
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"
}
在使用Alamofire.request
時(shí),可以傳入方法參數(shù):
Alamofire.request("https://httpbin.org/get") // 默認(rèn)是get請(qǐng)求
Alamofire.request("https://httpbin.org/post", method: .post)
Alamofire.request("https://httpbin.org/put", method: .put)
Alamofire.request("https://httpbin.org/delete", method: .delete)
參數(shù)編碼
Alamofire支持三種參數(shù)編碼:URL
、JSON
和PropertyList
。還支持遵循了ParameterEncoding
協(xié)議的自定義編碼。
URL編碼
URLEncoding
類型創(chuàng)建了一個(gè)URL編碼的查詢字符串來(lái)設(shè)置或者添加到一個(gè)現(xiàn)有的URL查詢字符串,或者設(shè)置URL請(qǐng)求的請(qǐng)求體。查詢字符串是否被設(shè)置或者添加到現(xiàn)有的URL查詢字符串,或者被作為HTTP請(qǐng)求體,決定于編碼的Destination
。編碼的Destination
有三個(gè)case:
-
.methodDependent
:為GET
、HEAD
和DELETE
請(qǐng)求使用編碼查詢字符串來(lái)設(shè)置或者添加到現(xiàn)有查詢字符串,并且使用其他HTTP方法來(lái)設(shè)置請(qǐng)求體。 -
.queryString
:設(shè)置或者添加編碼查詢字符串到現(xiàn)有查詢字符串 -
.httpBody
:把編碼查詢字符串作為URL請(qǐng)求的請(qǐng)求體
一個(gè)編碼請(qǐng)求的請(qǐng)求體的Content-Type
字段被設(shè)置為application/x-www-form-urlencoded; charset=utf-8
。因?yàn)闆](méi)有公開(kāi)的標(biāo)準(zhǔn)說(shuō)明如何編碼集合類型,所以按照慣例在key后面添加[]
來(lái)表示數(shù)組的值(foo[]=1&foo[]=2
),在key外面包一個(gè)中括號(hào)來(lái)表示字典的值(foo[bar]=baz
)。
使用URL編碼參數(shù)的GET請(qǐng)求
let parameters: Parameters = ["foo": "bar"]
// 下面這三種寫(xiě)法是等價(jià)的
Alamofire.request("https://httpbin.org/get", parameters: parameters) // encoding 默認(rèn)是`URLEncoding.default`
Alamofire.request("https://httpbin.org/get", parameters: parameters, encoding: URLEncoding.default)
Alamofire.request("https://httpbin.org/get", parameters: parameters, encoding: URLEncoding(destination: .methodDependent))
// https://httpbin.org/get?foo=bar
使用URL編碼參數(shù)的POST請(qǐng)求
let parameters: Parameters = [
"foo": "bar",
"baz": ["a", 1],
"qux": [
"x": 1,
"y": 2,
"z": 3
]
]
// 下面這三種寫(xiě)法是等價(jià)的
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters)
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: URLEncoding.default)
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: URLEncoding.httpBody)
// HTTP body: foo=bar&baz[]=a&baz[]=1&qux[x]=1&qux[y]=2&qux[z]=3
設(shè)置Bool
類型參數(shù)的編碼
URLEncoding.BoolEncoding
提供了兩種編碼方式:
-
.numeric
:把true
編碼為1
,false
編碼為0
-
.literal
:把true
編碼為true
,false
編碼為false
默認(rèn)情況下:Alamofire使用.numeric
。
可以使用下面的初始化函數(shù)來(lái)創(chuàng)建URLEncoding
,指定Bool編碼的類型:
let encoding = URLEncoding(boolEncoding: .literal)
設(shè)置Array
類型參數(shù)編碼
URLEncoding.ArrayEncoding
提供了兩種編碼方式:
-
.brackets
: 在每個(gè)元素值的key后面加上一個(gè)[]
,如foo=[1,2]
編碼成foo[]=1&foo[]=2
-
.noBrackets
:不添加[]
,例如foo=[1,2]
編碼成``foo=1&foo=2`
默認(rèn)情況下,Alamofire使用.brackets
。
可以使用下面的初始化函數(shù)來(lái)創(chuàng)建URLEncoding
,指定Array編碼的類型:
let encoding = URLEncoding(arrayEncoding: .noBrackets)
JSON編碼
JSONEncoding
類型創(chuàng)建了一個(gè)JOSN對(duì)象,并作為請(qǐng)求體。編碼請(qǐng)求的請(qǐng)求頭的Content-Type
請(qǐng)求字段被設(shè)置為application/json
。
使用JSON編碼參數(shù)的POST請(qǐng)求
let parameters: Parameters = [
"foo": [1,2,3],
"bar": [
"baz": "qux"
]
]
// 下面這兩種寫(xiě)法是等價(jià)的
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: JSONEncoding.default)
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: JSONEncoding(options: []))
// HTTP body: {"foo": [1, 2, 3], "bar": {"baz": "qux"}}
屬性列表編碼
PropertyListEncoding
根據(jù)關(guān)聯(lián)格式和寫(xiě)選項(xiàng)值,使用PropertyListSerialization
來(lái)創(chuàng)建一個(gè)屬性列表對(duì)象,并作為請(qǐng)求體。編碼請(qǐng)求的請(qǐng)求頭的Content-Type
請(qǐng)求字段被設(shè)置為application/x-plist
。
自定義編碼
如果提供的ParameterEncoding
類型不能滿足我們的要求,可以創(chuàng)建自定義編碼。下面演示如何快速自定義一個(gè)JSONStringArrayEncoding
類型把JSON字符串?dāng)?shù)組編碼到請(qǐng)求中。
struct JSONStringArrayEncoding: ParameterEncoding {
private let array: [String]
init(array: [String]) {
self.array = array
}
func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
var urlRequest = urlRequest.urlRequest
let data = try JSONSerialization.data(withJSONObject: array, options: [])
if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
}
urlRequest.httpBody = data
return urlRequest
}
}
手動(dòng)URL請(qǐng)求參數(shù)編碼
ParameterEncoding
API可以在創(chuàng)建網(wǎng)絡(luò)請(qǐng)求外面使用。
let url = URL(string: "https://httpbin.org/get")!
var urlRequest = URLRequest(url: url)
let parameters: Parameters = ["foo": "bar"]
let encodedURLRequest = try URLEncoding.queryString.encode(urlRequest, with: parameters)
HTTP請(qǐng)求頭
可以直接在請(qǐng)求方法添加自定義HTTP請(qǐng)求頭,這有利于我們?cè)谡?qǐng)求中添加請(qǐng)求頭。
let headers: HTTPHeaders = [
"Authorization": "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
"Accept": "application/json"
]
Alamofire.request("https://httpbin.org/headers", headers: headers).responseJSON { response in
debugPrint(response)
}
對(duì)于那些不變的請(qǐng)求頭,建議在URLSessionConfiguration
設(shè)置,這樣就可以自動(dòng)被用于任何URLSession
創(chuàng)建的URLSessionTask
。
默認(rèn)的Alamofire SessionManager
為每一個(gè)請(qǐng)求提供了一個(gè)默認(rèn)的請(qǐng)求頭集合,包括:
-
Accept-Encoding
,默認(rèn)是gzip;q=1.0, compress;q=0.5
。 -
Accept-Language
,默認(rèn)是系統(tǒng)的前6個(gè)偏好語(yǔ)言,格式類似于en;q=1.0
。 -
User-Agent
,包含當(dāng)前應(yīng)用程序的版本信息。例如iOS Example/1.0 (com.alamofire.iOS-Example; build:1; iOS 10.0.0) Alamofire/4.0.0
。
如果要自定義這些請(qǐng)求頭集合,我們必須創(chuàng)建一個(gè)自定義的URLSessionConfiguration
,defaultHTTPHeaders
屬性將會(huì)被更新,并且自定義的會(huì)話配置也會(huì)應(yīng)用到新的SessionManager
實(shí)例。
認(rèn)證
認(rèn)證是使用系統(tǒng)框架URLCredential
和URLAuthenticationChallenge
實(shí)現(xiàn)的。
支持的認(rèn)證方案
- HTTP Basic
- HTTP Digest
- Kerberos
- NTLM
HTTP Basic認(rèn)證
在合適的時(shí)候,在一個(gè)請(qǐng)求的authenticate
方法會(huì)自動(dòng)提供一個(gè)URLCredential
給URLAuthenticationChallenge
:
let user = "user"
let password = "password"
Alamofire.request("https://httpbin.org/basic-auth/\(user)/\(password)")
.authenticate(user: user, password: password)
.responseJSON { response in
debugPrint(response)
}
根據(jù)服務(wù)器實(shí)現(xiàn),Authorization
header也可能是適合的:
let user = "user"
let password = "password"
var headers: HTTPHeaders = [:]
if let authorizationHeader = Request.authorizationHeader(user: user, password: password) {
headers[authorizationHeader.key] = authorizationHeader.value
}
Alamofire.request("https://httpbin.org/basic-auth/user/password", headers: headers)
.responseJSON { response in
debugPrint(response)
}
使用URLCredential認(rèn)證
let user = "user"
let password = "password"
let credential = URLCredential(user: user, password: password, persistence: .forSession)
Alamofire.request("https://httpbin.org/basic-auth/\(user)/\(password)")
.authenticate(usingCredential: credential)
.responseJSON { response in
debugPrint(response)
}
注意:使用URLCredential
來(lái)做認(rèn)證,如果服務(wù)器發(fā)出一個(gè)challenge,底層的URLSession
實(shí)際上最終會(huì)發(fā)兩次請(qǐng)求。第一次請(qǐng)求不會(huì)包含credential,并且可能會(huì)觸發(fā)服務(wù)器發(fā)出一個(gè)challenge。這個(gè)challenge會(huì)被Alamofire接收,credential會(huì)被添加,然后URLSessin
會(huì)重試請(qǐng)求。
將數(shù)據(jù)下載到文件
Alamofire可以把服務(wù)器的數(shù)據(jù)下載到內(nèi)存(in-memory)或者硬盤(pán)(on-disk)中。所有Alamofire.request
API下載的數(shù)據(jù)都是存儲(chǔ)在內(nèi)存中。這比較適合小文件,更高效;但是不適合大文件,因?yàn)榇笪募?huì)把內(nèi)存耗盡。我們要使用Alamofire.download
API把服務(wù)器的數(shù)據(jù)下載到硬盤(pán)中。
下面這個(gè)方法只適用于macOS
。因?yàn)樵谄渌脚_(tái)不允許在應(yīng)用沙盒外訪問(wèn)文件系統(tǒng)。下面會(huì)講到如何在其他平臺(tái)下載文件。
Alamofire.download("https://httpbin.org/image/png").responseData { response in
if let data = response.result.value {
let image = UIImage(data: data)
}
}
下載文件存儲(chǔ)位置
我們可以提供一個(gè)DownloadFileDestination
閉包把臨時(shí)文件夾的文件移動(dòng)到一個(gè)目標(biāo)文件夾。在臨時(shí)文件真正移動(dòng)到destinationURL
之前,閉包內(nèi)部指定的DownloadOptions
將會(huì)被執(zhí)行。目前支持的DownloadOptions
有下面兩個(gè):
-
.createIntermediateDirectories
:如果指定了目標(biāo)URL,將會(huì)創(chuàng)建中間目錄。 -
.removePreviousFile
:如果指定了目標(biāo)URL,將會(huì)移除之前的文件
let destination: DownloadRequest.DownloadFileDestination = { _, _ in
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileURL = documentsURL.appendPathComponent("pig.png")
return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}
Alamofire.download(urlString, to: destination).response { response in
print(response)
if response.error == nil, let imagePath = response.destinationURL?.path {
let image = UIImage(contentsOfFile: imagePath)
}
}
也可以直接使用建議的下載目標(biāo)API:
let destination = DownloadRequest.suggestedDownloadDestination(directory: .documentDirectory)
Alamofire.download("https://httpbin.org/image/png", to: destination)
下載進(jìn)度
所有的DownloadRequest
都可以使用downloadProgress
API來(lái)反饋下載進(jìn)度。
Alamofire.download("https://httpbin.org/image/png")
.downloadProgress { progress in
print("Download Progress: \(progress.fractionCompleted)")
}
.responseData { response in
if let data = response.result.value {
let image = UIImage(data: data)
}
}
downloadProgress
API還可以接受一個(gè)queue
參數(shù)來(lái)指定下載進(jìn)度閉包在哪個(gè)DispatchQueue
中執(zhí)行。
let utilityQueue = DispatchQueue.global(qos: .utility)
Alamofire.download("https://httpbin.org/image/png")
.downloadProgress(queue: utilityQueue) { progress in
print("Download Progress: \(progress.fractionCompleted)")
}
.responseData { response in
if let data = response.result.value {
let image = UIImage(data: data)
}
}
恢復(fù)下載
如果一個(gè)DownloadRequest
被取消或中斷,底層的URL會(huì)話會(huì)生成一個(gè)恢復(fù)數(shù)據(jù)。恢復(fù)數(shù)據(jù)可以被重新利用并在中斷的位置繼續(xù)下載。恢復(fù)數(shù)據(jù)可以通過(guò)下載響應(yīng)訪問(wèn),然后在重新開(kāi)始請(qǐng)求的時(shí)候被利用。
重要:在iOS 10 - 10.2, macOS 10.12 - 10.12.2, tvOS 10 - 10.1, watchOS 3 - 3.1.1中,resumeData
會(huì)被后臺(tái)URL會(huì)話配置破壞。因?yàn)樵?code>resumeData的生成邏輯有一個(gè)底層的bug,不能恢復(fù)下載。具體情況可以到Stack Overflow看看。
class ImageRequestor {
private var resumeData: Data?
private var image: UIImage?
func fetchImage(completion: (UIImage?) -> Void) {
guard image == nil else { completion(image) ; return }
let destination: DownloadRequest.DownloadFileDestination = { _, _ in
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileURL = documentsURL.appendPathComponent("pig.png")
return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}
let request: DownloadRequest
if let resumeData = resumeData {
request = Alamofire.download(resumingWith: resumeData)
} else {
request = Alamofire.download("https://httpbin.org/image/png")
}
request.responseData { response in
switch response.result {
case .success(let data):
self.image = UIImage(data: data)
case .failure:
self.resumeData = response.resumeData
}
}
}
}
上傳數(shù)據(jù)到服務(wù)器
使用JOSN或者URL編碼參數(shù)上傳一些小數(shù)據(jù)到服務(wù)器,使用Alamofire.request
API就已經(jīng)足夠了。如果需要發(fā)送很大的數(shù)據(jù),需要使用Alamofire.upload
API。當(dāng)我們需要在后臺(tái)上傳數(shù)據(jù)時(shí),也可以使用Alamofire.upload
。
上傳數(shù)據(jù)
let imageData = UIPNGRepresentation(image)!
Alamofire.upload(imageData, to: "https://httpbin.org/post").responseJSON { response in
debugPrint(response)
}
上傳文件
let fileURL = Bundle.main.url(forResource: "video", withExtension: "mov")
Alamofire.upload(fileURL, to: "https://httpbin.org/post").responseJSON { response in
debugPrint(response)
}
上傳多部分表單數(shù)據(jù)
Alamofire.upload(
multipartFormData: { multipartFormData in
multipartFormData.append(unicornImageURL, withName: "unicorn")
multipartFormData.append(rainbowImageURL, withName: "rainbow")
},
to: "https://httpbin.org/post",
encodingCompletion: { encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload.responseJSON { response in
debugPrint(response)
}
case .failure(let encodingError):
print(encodingError)
}
}
)
上傳進(jìn)度
所有的UploadRequest
都可以使用uploadProgress
和downloadProgress
APIs來(lái)反饋上傳和下載進(jìn)度。
let fileURL = Bundle.main.url(forResource: "video", withExtension: "mov")
Alamofire.upload(fileURL, to: "https://httpbin.org/post")
.uploadProgress { progress in // 默認(rèn)在主線程中執(zhí)行
print("Upload Progress: \(progress.fractionCompleted)")
}
.downloadProgress { progress in // 默認(rèn)在主線程中執(zhí)行
print("Download Progress: \(progress.fractionCompleted)")
}
.responseJSON { response in
debugPrint(response)
}
統(tǒng)計(jì)指標(biāo)
時(shí)間表
Alamofire在一個(gè)請(qǐng)求周期內(nèi)收集時(shí)間,并創(chuàng)建一個(gè)Tineline
對(duì)象,它是響應(yīng)類型的一個(gè)屬性。
Alamofire.request("https://httpbin.org/get").responseJSON { response in
print(response.timeline)
}
上面的Timeline
信息包括:
- Latency: 0.428 seconds (延遲)
- Request Duration: 0.428 seconds (請(qǐng)求時(shí)間)
- Serialization Duration: 0.001 seconds (序列化時(shí)間)
- Total Duration: 0.429 seconds (總時(shí)間)
URL會(huì)話任務(wù)指標(biāo)
在iOS和tvOS 10和macOS 10.12中,蘋(píng)果發(fā)布了新的URLSessionTaskMetrics
APIs。這個(gè)任務(wù)指標(biāo)封裝了關(guān)于請(qǐng)求和響應(yīng)執(zhí)行的神奇統(tǒng)計(jì)信息。這個(gè)API和Timeline
非常相似,但是提供了很多Alamofire沒(méi)有提供的統(tǒng)計(jì)信息。這些指標(biāo)可以通過(guò)任何響應(yīng)去訪問(wèn)。
Alamofire.request("https://httpbin.org/get").responseJSON { response in
print(response.metrics)
}
注意:這些API只能在iOS和tvOS 10和macOS 10.12中使用。所以,根據(jù)部署目標(biāo),可能需要加入版本判斷:
Alamofire.request("https://httpbin.org/get").responseJSON { response in
if #available(iOS 10.0. *) {
print(response.metrics)
}
}
cURL命令輸出
調(diào)試平臺(tái)問(wèn)題很讓人厭煩。慶幸的是,Alamofire的Request
對(duì)象遵循了CustomStringConvertible
和CustomDebugStringConvertible
協(xié)議來(lái)提供一些非常有用的調(diào)試工具。
CustomStringConvertible
let request = Alamofire.request("https://httpbin.org/ip")
print(request)
// GET https://httpbin.org/ip (200)
CustomDebugStringConvertible
let request = Alamofire.request("https://httpbin.org/get", parameters: ["foo": "bar"])
debugPrint(request)
輸出:
$ curl -i \
-H "User-Agent: Alamofire/4.0.0" \
-H "Accept-Encoding: gzip;q=1.0, compress;q=0.5" \
-H "Accept-Language: en;q=1.0,fr;q=0.9,de;q=0.8,zh-Hans;q=0.7,zh-Hant;q=0.6,ja;q=0.5" \
"https://httpbin.org/get?foo=bar"
第一部分完。
【iOS開(kāi)發(fā)】Alamofire框架的使用二 —— 高級(jí)用法 >>
如果想第一時(shí)間關(guān)注我的文章,可訪問(wèn)我的個(gè)人博客網(wǎng)站:曾文志 - iOS開(kāi)發(fā)者,專注Swift。,拉到最下面訂閱我的博客,并記得在郵件中確認(rèn)訂閱。謝謝!
歡迎加入我管理的Swift開(kāi)發(fā)群:536353151
。