即將離開簡(jiǎn)書练对,請(qǐng)到掘金繼續(xù)關(guān)注我悍抑。謝謝栅哀!
本文掘金鏈接
為什么離開
此文章是對(duì) Alamofire Usage 的翻譯滥壕,有需要的可以去看原文。
另外此文章的內(nèi)容也保存到了我的 GitHub 倉(cāng)庫(kù)绍绘,建議去 GitHub 閱讀,以獲得更好的閱讀體驗(yàn)迟赃。如果覺得對(duì)你有用的陪拘,可以順手給個(gè) Star。謝謝纤壁!
- Alamofire 5 的使用 - 基本用法
Alamofire 5 的使用 - 基本用法
我分兩篇文章介紹如何使用 Alamofire 5左刽。文章的內(nèi)容主要是翻譯 Alamofire 的 readme。第二篇文章 >>
特性
- 可鏈接的請(qǐng)求/響應(yīng)函數(shù)
- URL / JSON 參數(shù)編碼
- 上傳文件 / Data / 流 / 多表單數(shù)據(jù)
- 使用請(qǐng)求或者恢復(fù)數(shù)據(jù)下載文件
- 使用 URLCredential 進(jìn)行身份驗(yàn)證
- HTTP 響應(yīng)驗(yàn)證
- 帶有進(jìn)度的上傳和下載閉包
- cURL 命令的輸出
- 動(dòng)態(tài)調(diào)整和重試請(qǐng)求
- TLS 證書和公鑰固定
- 網(wǎng)絡(luò)可達(dá)性
- 全面的單元和集成測(cè)試覆蓋率
組件庫(kù)
為了讓 Alamofire 專注于核心網(wǎng)絡(luò)實(shí)現(xiàn)酌媒,Alamofire 軟件基金會(huì)創(chuàng)建了額外的組件庫(kù)欠痴,為 Alamofire 生態(tài)系統(tǒng)帶來額外的功能迄靠。
-
AlamofireImage:一個(gè)圖片庫(kù),包括圖像響應(yīng)序列化器喇辽、
UIImage
和UIImageView
的擴(kuò)展掌挚、自定義圖像濾鏡、自動(dòng)清除內(nèi)存緩存和基于優(yōu)先級(jí)的圖像下載系統(tǒng)菩咨。 - AlamofireNetworkActivityIndicator: 使用 Alamofire 控制 iOS 上網(wǎng)絡(luò)活動(dòng)指示器的可見性吠式。它包含可配置的延遲計(jì)時(shí)器,有助于減少閃爍旦委,并且可以支持不由 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'
end
iOS 版本和 Alamofire
版本可以自己根據(jù)實(shí)際情況自行更改。CocoaPods 是比較常用的第三方庫(kù)管理工具缨硝,其他方法就不詳細(xì)說了摩钙。其他集成方法可以查看原文檔。
基本用法
介紹
Alamofire 為 HTTP 網(wǎng)絡(luò)請(qǐng)求提供了一個(gè)優(yōu)雅且可組合的接口查辩。它沒有實(shí)現(xiàn)自己的 HTTP 網(wǎng)絡(luò)功能胖笛。取而代之的是,它建立在由 Foundation 框架提供的 URL 加載系統(tǒng)之上宜岛。系統(tǒng)的核心是 URLSession
和 URLSessionTask
子類长踊。Alamofire 將這些 APIs 和許多其他 APIs 封裝在一個(gè)更易于使用的接口中,并提供使用 HTTP 網(wǎng)絡(luò)進(jìn)行現(xiàn)代應(yīng)用程序開發(fā)所必需的各種功能萍倡。但是身弊,了解 Alamofire 的許多核心行為來自何處很重要,因此熟悉 URL 加載系統(tǒng)非常重要列敲。歸根結(jié)底阱佛,Alamofire 的網(wǎng)絡(luò)特性受到該系統(tǒng)功能的限制,應(yīng)該始終記住并遵守其行為和最佳實(shí)踐戴而。
此外凑术,Alamofire(以及 URL 加載系統(tǒng))中的聯(lián)網(wǎng)是異步完成的。異步編程可能會(huì)讓不熟悉這個(gè)概念的程序員感到沮喪所意,但是有很好的理由這樣做淮逊。
另外:AF
命名空間和引用
以前的 Alamofire 文檔使用了類似 Alamofire.request()
的示例。這個(gè) API 雖然看起來需要 Alamofire
前綴扶踊,但實(shí)際上在沒有它的情況下也可以泄鹏。request
方法和其他函數(shù)在任何帶有 import Alamofire
的文件中都是全局可用的。從 Alamofire 5 開始秧耗,此功能已被刪除命满,被更改為 AF
,它是對(duì) Session.default
的引用绣版。這允許 Alamofire 提供同樣的便利功能胶台,同時(shí)不必每次使用 Alamofire 時(shí)都污染全局命名空間歼疮,也不必全局復(fù)制 Session
API。類似地诈唬,由 Alamofire 擴(kuò)展的類型將使用 af
屬性擴(kuò)展來將 Alamofire 添加的功能與其他擴(kuò)展分開韩脏。
發(fā)起請(qǐng)求
Alamofire 為發(fā)出 HTTP 請(qǐng)求提供了多種方便的方法。最簡(jiǎn)單的是铸磅,只需提供一個(gè)可以轉(zhuǎn)換為 URL 的 String
:
AF.request("https://httpbin.org/get").response { response in
debugPrint(response)
}
所有示例都需要在源文件中的某個(gè)位置
import Alamofire
赡矢。
這實(shí)際上是 Alamofire Session
類型上用于發(fā)出請(qǐng)求的兩個(gè)頂層 APIs 的一種形式。它的完整定義如下:
open func request<Parameters: Encodable>(
_ convertible: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoder: ParameterEncoder = URLEncodedFormParameterEncoder.default,
headers: HTTPHeaders? = nil,
interceptor: RequestInterceptor? = nil
) -> DataRequest
此方法創(chuàng)建一個(gè) DataRequest
阅仔,同時(shí)允許組合來自各個(gè)組件(如 method
和 headers
)的請(qǐng)求吹散,同時(shí)還允許每個(gè)傳入 RequestInterceptors
和 Encodable
參數(shù)。
還有其他方法允許您使用
Parameters
字典和ParameterEncoding
類型來發(fā)出請(qǐng)求八酒。不再推薦此 API空民,最終將被棄用并從 Alamofire 中刪除。
這個(gè) API 的第二個(gè)版本要簡(jiǎn)單得多:
open func request(
_ urlRequest: URLRequestConvertible,
interceptor: RequestInterceptor? = nil
) -> DataRequest
此方法為遵循 Alamofire
的 URLRequestConvertible
協(xié)議的任何類型創(chuàng)建 DataRequest
羞迷。所有不同于前一版本的參數(shù)都封裝在該值中界轩,這會(huì)產(chǎn)生非常強(qiáng)大的抽象。這將在我們的高級(jí)用法中討論衔瓮。
HTTP Methods
HTTPMethod
類型列出了 RFC 7231 §4.3 中定義的 HTTP 方法:
public struct HTTPMethod: RawRepresentable, Equatable, Hashable {
public static let connect = HTTPMethod(rawValue: "CONNECT")
public static let delete = HTTPMethod(rawValue: "DELETE")
public static let get = HTTPMethod(rawValue: "GET")
public static let head = HTTPMethod(rawValue: "HEAD")
public static let options = HTTPMethod(rawValue: "OPTIONS")
public static let patch = HTTPMethod(rawValue: "PATCH")
public static let post = HTTPMethod(rawValue: "POST")
public static let put = HTTPMethod(rawValue: "PUT")
public static let trace = HTTPMethod(rawValue: "TRACE")
public let rawValue: String
public init(rawValue: String) {
self.rawValue = rawValue
}
}
這些值可以作為 method
參數(shù)傳遞給 AF.request
API:
AF.request("https://httpbin.org/get")
AF.request("https://httpbin.org/post", method: .post)
AF.request("https://httpbin.org/put", method: .put)
AF.request("https://httpbin.org/delete", method: .delete)
重要的是要記住浊猾,不同的 HTTP 方法可能有不同的語(yǔ)義,需要不同的參數(shù)編碼热鞍,這取決于服務(wù)器的期望葫慎。例如,URLSession 或 Alamofire 不支持在 GET
請(qǐng)求中傳遞 body
數(shù)據(jù)薇宠,并將返回錯(cuò)誤偷办。
Alamofire 還提供了對(duì) URLRequest
的擴(kuò)展,以橋接將字符串返回到 HTTPMethod
值的 httpMethod
屬性:
public extension URLRequest {
/// Returns the `httpMethod` as Alamofire's `HTTPMethod` type.
var method: HTTPMethod? {
get { return httpMethod.flatMap(HTTPMethod.init) }
set { httpMethod = newValue?.rawValue }
}
}
如果需要使用 Alamofire 的 HTTPMethod
類型不支持的 HTTP 方法昼接,可以擴(kuò)展該類型以添加自定義值:
extension HTTPMethod {
static let custom = HTTPMethod(rawValue: "CUSTOM")
}
請(qǐng)求參數(shù)和參數(shù)編碼器
Alamofire 支持將任何 Encodable
類型作為請(qǐng)求的參數(shù)。然后悴晰,這些參數(shù)通過遵循 ParameterEncoder
協(xié)議的類型傳遞慢睡,并添加到 URLRequest
中,然后通過網(wǎng)絡(luò)發(fā)送铡溪。Alamofire 包含兩種遵循 ParameterEncoder
的類型:JSONParameterEncoder
和 URLEncodedFormParameterEncoder
漂辐。這些類型涵蓋了現(xiàn)代服務(wù)使用的最常見的編碼。
struct Login: Encodable {
let email: String
let password: String
}
let login = Login(email: "test@test.test", password: "testPassword")
AF.request("https://httpbin.org/post",
method: .post,
parameters: login,
encoder: JSONParameterEncoder.default).response { response in
debugPrint(response)
}
URLEncodedFormParameterEncoder
URLEncodedFormParameterEncoder
將值編碼為 URL 編碼字符串棕硫,以將其設(shè)置為或附加到任何現(xiàn)有 URL 查詢字符串髓涯,或設(shè)置為請(qǐng)求的 HTTP body。通過設(shè)置編碼的目的地哈扮,可以控制編碼字符串的設(shè)置位置纬纪。URLEncodedFormParameterEncoder.Destination
枚舉有三種情況:
-
.methodDependent
- 對(duì)于.get
蚓再、.head
激涤、.delete
請(qǐng)求呢灶,它會(huì)將已編碼查詢字符串應(yīng)用到現(xiàn)有的查詢字符串中;對(duì)于其他類型的請(qǐng)求叮叹,會(huì)將其設(shè)置為 HTTP body问畅。 -
.queryString
- 將編碼字符串設(shè)置或追加到請(qǐng)求的 URL 中娃属。 -
.httpBody
- 將編碼字符串設(shè)置為URLRequest
的 HTTP body。
如果尚未設(shè)置 Content-Type
护姆,那么會(huì)把具有 HTTP body 的已編碼請(qǐng)求的 HTTP header 設(shè)置為 application/x-www-form-urlencoded; charset=utf-8
矾端。
在內(nèi)部,URLEncodedFormParameterEncoder
使用 URLEncodedFormEncoder
把 Encodable
類型編碼為 URL 編碼形式的 String
卵皂。此編碼器可用于自定義各種類型的編碼秩铆,包括使用 ArrayEncoding
的 Array
、使用 BoolEncoding
的Bool
渐裂、使用 DataEncoding
的 Data
豺旬、使用 DateEncoding
的 Date
、使用 KeyEncoding
的 keys 以及使用 SpaceEncoding
的空格柒凉。
使用 URL 編碼參數(shù)的 GET 請(qǐng)求
let parameters = ["foo": "bar"]
// 下面三種方法都是等價(jià)的
AF.request("https://httpbin.org/get", parameters: parameters) // encoding defaults to `URLEncoding.default`
AF.request("https://httpbin.org/get", parameters: parameters, encoder: URLEncodedFormParameterEncoder.default)
AF.request("https://httpbin.org/get", parameters: parameters, encoder: URLEncodedFormParameterEncoder(destination: .methodDependent))
// https://httpbin.org/get?foo=bar
使用 URL 編碼參數(shù)的 POST 請(qǐng)求
let parameters: [String: [String]] = [
"foo": ["bar"],
"baz": ["a", "b"],
"qux": ["x", "y", "z"]
]
// 下面三種方法都是等價(jià)的
AF.request("https://httpbin.org/post", method: .post, parameters: parameters)
AF.request("https://httpbin.org/post", method: .post, parameters: parameters, encoder: URLEncodedFormParameterEncoder.default)
AF.request("https://httpbin.org/post", method: .post, parameters: parameters, encoder: URLEncodedFormParameterEncoder(destination: .httpBody))
// HTTP body: "qux[]=x&qux[]=y&qux[]=z&baz[]=a&baz[]=b&foo[]=bar"
配置已編碼參數(shù)的排序
從 Swift 4.2 開始族阅,Swift 的 Dictionary
類型使用的隨機(jī)算法在運(yùn)行時(shí)產(chǎn)生一個(gè)隨機(jī)的內(nèi)部順序,并且在應(yīng)用程序的每次啟動(dòng)都是不同的膝捞。這可能會(huì)導(dǎo)致已編碼參數(shù)更改順序坦刀,這可能會(huì)影響緩存和其他行為。默認(rèn)情況下蔬咬,URLEncodedFormEncoder
將對(duì)其編碼的鍵值對(duì)進(jìn)行排序鲤遥。雖然這會(huì)為所有 Encodable
類型生成常量輸出,但它可能與該類型實(shí)現(xiàn)的實(shí)際編碼順序不匹配林艘。您可以將 alphabetizeKeyValuePairs
設(shè)置為 false
以返回到實(shí)現(xiàn)的順序盖奈,因此這將變成隨機(jī) Dictionary
順序。
您可以創(chuàng)建自己的 URLEncodedFormParameterEncoder
狐援,在初始化時(shí)钢坦,可以在 URLEncodedFormEncoder
參數(shù)中設(shè)置 alphabetizeKeyValuePairs
的值:
let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(alphabetizeKeyValuePairs: false))
配置 Array
參數(shù)的編碼
由于沒有關(guān)于如何對(duì)集合類型進(jìn)行編碼的規(guī)范,默認(rèn)情況下啥酱,Alamofire 遵循以下約定:將 []
附加到數(shù)組值的鍵(foo[]=1&foo[]=2
)爹凹,并附加由中括號(hào)包圍的嵌套字典值的鍵(foo[bar]=baz
)。
URLEncodedFormEncoder.ArrayEncoding
枚舉提供了以下對(duì)數(shù)組參數(shù)進(jìn)行編碼的方法:
-
.brackets
- 為每個(gè)值在鍵后附加一組空的中括號(hào)镶殷。這是默認(rèn)情況禾酱。 -
.noBrackets
- 不附加括號(hào)。key 按原樣編碼。
默認(rèn)情況下颤陶,Alamofire 使用 .brackets
編碼颗管,其中 foo = [1, 2]
編碼為 foo[]=1&foo[]=2
。
使用 .noBrackets
編碼指郁,foo = [1, 2]
編碼為 foo=1&foo=2
忙上。
您可以創(chuàng)建自己的 URLEncodedFormParameterEncoder
,在初始化時(shí)闲坎,可以在 URLEncodedFormEncoder
參數(shù)中設(shè)置 arrayEncoding
的值:
let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(arrayEncoding: .noBrackets))
配置 Bool
參數(shù)的編碼
URLEncodedFormEncoder.BoolEncoding
枚舉提供了以下用于編碼 Bool
參數(shù)的方法:
-
.numeric
- 把true
編碼為1
疫粥,false
編碼為0
。這是默認(rèn)情況腰懂。 -
.literal
- 把true
和false
編碼為字符串文本梗逮。
默認(rèn)情況下,Alamofire 使用 .numeric
绣溜。
您可以創(chuàng)建自己的 URLEncodedFormParameterEncoder
慷彤,在初始化時(shí),可以在 URLEncodedFormEncoder
參數(shù)中設(shè)置 boolEncoding
的值:
let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(boolEncoding: .numeric))
配置 Data
參數(shù)的編碼
DataEncoding
包括以下用于編碼 Data
參數(shù)的方法:
-
.deferredToData
- 使用Data
的自帶Encodable
支持怖喻。 -
.base64
- 將Data
編碼為 base64 編碼的字符串底哗。這是默認(rèn)情況。 -
.custom((Data) -> throws -> String)
- 使用給定的閉包對(duì)Data
進(jìn)行編碼锚沸。
您可以創(chuàng)建自己的 URLEncodedFormParameterEncoder
跋选,在初始化時(shí),可以在 URLEncodedFormEncoder
參數(shù)中設(shè)置 dataEncoding
的值:
let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(dataEncoding: .base64))
配置 Date
參數(shù)的編碼
鑒于將 Date
編碼為 String
的方法非常多哗蜈,DateEncoding
包括以下用于編碼 Date
參數(shù)的方法:
-
.deferredToDate
- 使用Date
的自帶Encodable
支持前标。這是默認(rèn)情況。 -
.secondsSince1970
- 將Date
編碼為 1970 年 1 月 1 日 UTC 零點(diǎn)的秒數(shù)距潘。 -
.millisecondsSince1970
- 將Date
編碼為 1970 年 1 月 1 日 UTC 零點(diǎn)的毫秒數(shù)炼列。 -
.iso8601
- 根據(jù) ISO 8601 和 RFC3339 標(biāo)準(zhǔn)對(duì)Date
進(jìn)行編碼。 -
.formatted(DateFormatter)
- 使用給定的DateFormatter
對(duì)Date
進(jìn)行編碼音比。 -
.custom((Date) throws -> String)
- 使用給定的閉包對(duì)Date
進(jìn)行編碼俭尖。
您可以創(chuàng)建自己的 URLEncodedFormParameterEncoder
,在初始化時(shí)洞翩,可以在 URLEncodedFormEncoder
參數(shù)中設(shè)置 dateEncoding
的值:
let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(dateEncoding: .iso8601))
配置 Coding Keys 的編碼
由于 key 參數(shù)樣式的多樣性稽犁,KeyEncoding
提供了以下方法來從 lowerCamelCase
中自定義 key 編碼:
-
.useDefaultKeys
- 使用每種類型指定的 key。這是默認(rèn)情況菱农。 -
.convertToSnakeCase
- 將 key 轉(zhuǎn)換為 snake case:oneTwoThree
變成one_two_three
缭付。 -
.convertToKebabCase
- 將 key 轉(zhuǎn)換為 kebab case:oneTwoThree
變成one-two-three
柿估。 -
.capitalized
- 將第一個(gè)字母大寫循未,例如oneTwoThree
變?yōu)?OneTwoThree
。 -
.uppercased
- 所有字母大寫:oneTwoThree
變成ONETWOTHREE
。 -
.lowercased
- 所有字母小寫:oneTwoThree
變成onetwothree
的妖。 -
.custom((String) -> String)
- 使用給定的閉包對(duì) key 進(jìn)行編碼绣檬。
您可以創(chuàng)建自己的 URLEncodedFormParameterEncoder
,在初始化時(shí)嫂粟,可以在 URLEncodedFormEncoder
參數(shù)中設(shè)置 keyEncoding
的值:
let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(keyEncoding: .convertToSnakeCase))
配置空格的編碼
舊的表單編碼器使用 +
來對(duì)空格進(jìn)行編碼娇未,而一些服務(wù)器仍然希望使用這種編碼,而不是現(xiàn)代的百分比編碼星虹,因此 Alamofire 包含以下對(duì)空格進(jìn)行編碼的方法:
.percentEscaped
- 通過應(yīng)用標(biāo)準(zhǔn)百分比轉(zhuǎn)義對(duì)空格字符進(jìn)行編碼零抬。" "
編碼為"%20"
。這是默認(rèn)情況宽涌。
.plusReplaced
- 將空格字符替換為 +
平夜," "
編碼為"+"
。
您可以創(chuàng)建自己的 URLEncodedFormParameterEncoder
卸亮,在初始化時(shí)忽妒,可以在 URLEncodedFormEncoder
參數(shù)中設(shè)置 spaceEncoding
的值:
let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(spaceEncoding: .plusReplaced))
JSONParameterEncoder
JSONParameterEncoder
使用 Swift 的 JSONEncoder
對(duì) Encodable
值進(jìn)行編碼,并將結(jié)果設(shè)置為 URLRequest
的 httpBody
兼贸。如果 Content-Type
尚未設(shè)置段直,則將其設(shè)置為 application/json
。
JSON 編碼參數(shù)的 POST 請(qǐng)求
let parameters: [String: [String]] = [
"foo": ["bar"],
"baz": ["a", "b"],
"qux": ["x", "y", "z"]
]
AF.request("https://httpbin.org/post", method: .post, parameters: parameters, encoder: JSONParameterEncoder.default)
AF.request("https://httpbin.org/post", method: .post, parameters: parameters, encoder: JSONParameterEncoder.prettyPrinted)
AF.request("https://httpbin.org/post", method: .post, parameters: parameters, encoder: JSONParameterEncoder.sortedKeys)
// HTTP body: {"baz":["a","b"],"foo":["bar"],"qux":["x","y","z"]}
自定義 JSONEncoder
您可以自定義 JSONParameterEncoder
的行為溶诞,方法是將自定義的 JSONEncoder
實(shí)例傳遞給它:
let encoder = JSONEncoder()
encoder.dateEncoding = .iso8601
encoder.keyEncodingStrategy = .convertToSnakeCase
let parameterEncoder = JSONParameterEncoder(encoder: encoder)
手動(dòng)對(duì) URLRequest
進(jìn)行參數(shù)編碼
ParameterEncoder
APIs 也可以在 Alamofire 之外使用鸯檬,方法是直接在URLRequest
中編碼參數(shù)。
let url = URL(string: "https://httpbin.org/get")!
var urlRequest = URLRequest(url: url)
let parameters = ["foo": "bar"]
let encodedURLRequest = try URLEncodedFormParameterEncoder.default.encode(parameters, into: urlRequest)
HTTP Headers
Alamofire 包含自己的 HTTPHeaders
類型很澄,這是一種順序保持且不區(qū)分大小寫的 HTTP header name/value 對(duì)的表示京闰。HTTPHeader
類型封裝單個(gè) name/value 對(duì),并為常用的 headers 提供各種靜態(tài)值甩苛。
向 Request
添加自定義 HTTPHeaders
就像向 request
方法傳遞值一樣簡(jiǎn)單:
let headers: HTTPHeaders = [
"Authorization": "Basic VXNlcm5hbWU6UGFzc3dvcmQ=",
"Accept": "application/json"
]
AF.request("https://httpbin.org/headers", headers: headers).responseJSON { response in
debugPrint(response)
}
HTTPHeaders
也可以由 HTTPHeader
數(shù)組構(gòu)造:
let headers: HTTPHeaders = [
.authorization(username: "Username", password: "Password"),
.accept("application/json")
]
AF.request("https://httpbin.org/headers", headers: headers).responseJSON { response in
debugPrint(response)
}
對(duì)于不會(huì)變的 HTTP headers蹂楣,建議在
URLSessionConfiguration
上設(shè)置它們,以便讓它們自動(dòng)應(yīng)用于底層URLSession
創(chuàng)建的任何URLSessionTask
讯蒲。
默認(rèn)的 Alamofire Session
為每個(gè) Request
提供一組默認(rèn)的 headers痊土。其中包括:
-
Accept-Encoding
,默認(rèn)為br;q=1.0, gzip;q=0.8, deflate;q=0.6
墨林,根據(jù)RFC 7230 §4.2.3
赁酝。 -
Accept-Language
,默認(rèn)為系統(tǒng)中最多 6 種首選語(yǔ)言旭等,格式為en;q=1.0
酌呆,根據(jù)RFC 7231 §5.3.5
。 -
User-Agent
搔耕,其中包含有關(guān)當(dāng)前應(yīng)用程序的版本信息隙袁。例如:iOS Example/1.0 (com.alamofire.iOS-Example; build:1; iOS 13.0.0) Alamofire/5.0.0
,根據(jù)RFC 7231 §5.5.3
。
如果需要自定義這些 headers菩收,則應(yīng)創(chuàng)建自定義 URLSessionConfiguration
梨睁,更新 defaultHTTPHeaders
屬性,并將配置應(yīng)用于新 Session
實(shí)例娜饵。使用URLSessionConfiguration.af.default
來自定義配置坡贺,會(huì)保留 Alamofire 的默認(rèn) headers。
響應(yīng)驗(yàn)證
默認(rèn)情況下箱舞,無論響應(yīng)的內(nèi)容如何遍坟,Alamofire 都會(huì)將任何已完成的請(qǐng)求視為成功。如果響應(yīng)具有不可接受的狀態(tài)代碼或 MIME 類型晴股,則在響應(yīng)處理程序之前調(diào)用 validate()
將導(dǎo)致生成錯(cuò)誤政鼠。
自動(dòng)驗(yàn)證
validate()
API 自動(dòng)驗(yàn)證狀態(tài)代碼是否在 200..<300
范圍內(nèi),以及響應(yīng)的 Content-Type
header 是否與請(qǐng)求的 Accept
匹配(如果有提供)队魏。
AF.request("https://httpbin.org/get").validate().responseJSON { response in
debugPrint(response)
}
手動(dòng)驗(yàn)證
AF.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 let .failure(error):
print(error)
}
}
響應(yīng)處理
Alamofire 的 DataRequest
和 DownloadRequest
都有相應(yīng)的響應(yīng)類型:DataResponse<Success, Failure: Error>
和 DownloadResponse<Success, Failure: Error>
公般。這兩個(gè)類型都由兩個(gè)泛型組成:序列化類型和錯(cuò)誤類型。默認(rèn)情況下胡桨,所有響應(yīng)值都將生成 AFError
錯(cuò)誤類型(DataResponse<Success, AFError>
)官帘。Alamofire 在其公共 API 中使用了更簡(jiǎn)單的 AFDataResponse<Success>
和 AFDownloadResponse<Success>
,它們總是有 AFError
錯(cuò)誤類型昧谊。UploadRequest
是 DataRequest
的一個(gè)子類刽虹,使用相同的 DataResponse
類型。
處理在 Alamofire 中發(fā)出的 DataRequest
或 UploadRequest
的 DataResponse
涉及到鏈接 response handler呢诬,例如 responseJSON
鏈接到 DataRequest
:
AF.request("https://httpbin.org/get").responseJSON { response in
debugPrint(response)
}
在上面的示例中涌哲,responseJSON
handler 被添加到 DataRequest
中,以便在 DataRequest
完成后執(zhí)行尚镰。傳遞給 handler 閉包的參數(shù)是從響應(yīng)屬性來的 JSONResponseSerializer
生成的 AFDataResponse<Any>
值阀圾。
此閉包并不阻塞執(zhí)行以等待服務(wù)器的響應(yīng),而是作為回調(diào)添加狗唉,以便在收到響應(yīng)后處理該響應(yīng)初烘。請(qǐng)求的結(jié)果僅在響應(yīng)閉包的范圍內(nèi)可用。任何依賴于從服務(wù)器接收到的響應(yīng)或數(shù)據(jù)的執(zhí)行都必須在響應(yīng)閉包中完成分俯。
Alamofire 的網(wǎng)絡(luò)請(qǐng)求是異步完成的肾筐。異步編程可能會(huì)讓不熟悉這個(gè)概念的程序員感到沮喪,但是有很好的理由這樣做缸剪。
默認(rèn)情況下吗铐,Alamofire 包含六個(gè)不同的數(shù)據(jù)響應(yīng) handlers,包括:
// Response Handler - 未序列化的 Response
func response(
queue: DispatchQueue = .main,
completionHandler: @escaping (AFDataResponse<Data?>) -> Void
) -> Self
// Response Serializer Handler - Serialize using the passed Serializer
func response<Serializer: DataResponseSerializerProtocol>(
queue: DispatchQueue = .main,
responseSerializer: Serializer,
completionHandler: @escaping (AFDataResponse<Serializer.SerializedObject>) -> Void
) -> Self
// Response Data Handler - Serialized into Data
func responseData(
queue: DispatchQueue = .main,
completionHandler: @escaping (AFDataResponse<Data>) -> Void
) -> Self
// Response String Handler - Serialized into String
func responseString(
queue: DispatchQueue = .main,
encoding: String.Encoding? = nil,
completionHandler: @escaping (AFDataResponse<String>) -> Void
) -> Self
// Response JSON Handler - Serialized into Any Using JSONSerialization
func responseJSON(
queue: DispatchQueue = .main,
options: JSONSerialization.ReadingOptions = .allowFragments,
completionHandler: @escaping (AFDataResponse<Any>) -> Void
) -> Self
// Response Decodable Handler - Serialized into Decodable Type
func responseDecodable<T: Decodable>(
of type: T.Type = T.self,
queue: DispatchQueue = .main,
decoder: DataDecoder = JSONDecoder(),
completionHandler: @escaping (AFDataResponse<T>) -> Void
) -> Self
沒有一個(gè)響應(yīng) handlers 對(duì)從服務(wù)器返回的 HTTPURLResponse
執(zhí)行任何驗(yàn)證杏节。
例如唬渗,
400..<500
和500..<600
范圍內(nèi)的響應(yīng)狀態(tài)代碼不會(huì)自動(dòng)觸發(fā)錯(cuò)誤讥此。Alamofire 使用響應(yīng)驗(yàn)證鏈接來實(shí)現(xiàn)這一點(diǎn)。
響應(yīng) Handler
響應(yīng) handler 不計(jì)算任何響應(yīng)數(shù)據(jù)谣妻。它只是直接從 URLSessionDelegate
轉(zhuǎn)發(fā)所有信息。它相當(dāng)于使用 cURL
執(zhí)行請(qǐng)求卒稳。
AF.request("https://httpbin.org/get").response { response in
debugPrint("Response: \(response)")
}
我們強(qiáng)烈建議您利用 Response
和 Result
類型來利用其他響應(yīng)序列化器蹋半。
響應(yīng) Data Handler
responseData
handler 使用 DataResponseSerializer
提取并驗(yàn)證服務(wù)器返回的數(shù)據(jù)。如果沒有發(fā)生錯(cuò)誤并且返回?cái)?shù)據(jù)充坑,則響應(yīng)結(jié)果將為 .success
减江, value
將為從服務(wù)器返回的 Data
。
AF.request("https://httpbin.org/get").responseData { response in
debugPrint("Response: \(response)")
}
響應(yīng) String Handler
responseString
handler 使用 StringResponseSerializer
將服務(wù)器返回的數(shù)據(jù)轉(zhuǎn)換為具有指定編碼的 String
捻爷。如果沒有發(fā)生錯(cuò)誤辈灼,并且服務(wù)器數(shù)據(jù)成功序列化為 String
,則響應(yīng)結(jié)果將為 .success
也榄,并且值的類型為 String
巡莹。
AF.request("https://httpbin.org/get").responseString { response in
debugPrint("Response: \(response)")
}
如果未指定編碼,Alamofire 將使用服務(wù)器
HTTPURLResponse
中指定的文本編碼甜紫。如果服務(wù)器響應(yīng)無法確定文本編碼降宅,則默認(rèn)為.isoLatin1
。
響應(yīng) JSON Handler
responseJSON
handler 使用 JSONResponseSerializer
使用指定的 JSONSerialization.ReadingOptions
將服務(wù)器返回的數(shù)據(jù)轉(zhuǎn)換為 Any
類型囚霸。如果沒有出現(xiàn)錯(cuò)誤腰根,并且服務(wù)器數(shù)據(jù)成功序列化為 JSON 對(duì)象,則響應(yīng) AFResult
將為 .success
拓型,值將為 Any
類型额嘿。
AF.request("https://httpbin.org/get").responseJSON { response in
debugPrint("Response: \(response)")
}
響應(yīng) Decodable
Handler
responseDecodable
handler 使用 DecodableResponseSerializer
和 指定的 DataDecoder
(Decoder
的協(xié)議抽象,可以從 Data
解碼)將服務(wù)器返回的數(shù)據(jù)轉(zhuǎn)換為傳遞進(jìn)來的 Decodable
類型劣挫。如果沒有發(fā)生錯(cuò)誤册养,并且服務(wù)器數(shù)據(jù)已成功解碼為 Decodable
類型,則響應(yīng) Result
將為 .success
压固,并且 value
將為傳遞進(jìn)來的類型捕儒。
struct HTTPBinResponse: Decodable {
let url: String
}
AF.request("https://httpbin.org/get").responseDecodable(of: HTTPBinResponse.self) { response in
debugPrint("Response: \(response)")
}
鏈?zhǔn)巾憫?yīng) handlers
響應(yīng) handlers 還可以連接起來:
Alamofire.request("https://httpbin.org/get")
.responseString { response in
print("Response String: \(response.value)")
}
.responseJSON { response in
print("Response JSON: \(response.value)")
}
需要注意的是,對(duì)同一請(qǐng)求使用多個(gè)響應(yīng) handlers 需要多次序列化服務(wù)器數(shù)據(jù)邓夕,每個(gè)響應(yīng) handlers 處理一次刘莹。作為最佳實(shí)踐,通常應(yīng)避免對(duì)同一請(qǐng)求使用多個(gè)響應(yīng) handlers焚刚,特別是在生產(chǎn)環(huán)境中点弯。它們只能用于調(diào)試或在沒有更好選擇的情況下使用。
響應(yīng) Handler 隊(duì)列
默認(rèn)情況下矿咕,傳遞給響應(yīng) handler 的閉包在 .main
隊(duì)列上執(zhí)行抢肛,但可以傳遞一個(gè)指定的 DispatchQueue 來執(zhí)行閉包狼钮。實(shí)際的序列化工作(將 Data
轉(zhuǎn)換為其他類型)總是在后臺(tái)隊(duì)列上執(zhí)行。
let utilityQueue = DispatchQueue.global(qos: .utility)
AF.request("https://httpbin.org/get").responseJSON(queue: utilityQueue) { response in
print("Executed on utility queue.")
debugPrint(response)
}
響應(yīng)緩存
響應(yīng)緩存使用系統(tǒng)自帶的 URLCache
處理捡絮。它提供了內(nèi)存和磁盤上的復(fù)合緩存熬芜,并允許您管理用于緩存的內(nèi)存和磁盤的大小。
默認(rèn)情況下福稳,Alamofire 利用
URLCache.shared
實(shí)例涎拉。要自定義使用的URLCache
實(shí)例,請(qǐng)查看高級(jí)用法的圆。
身份驗(yàn)證
身份驗(yàn)證使用系統(tǒng)自帶的 URLCredential
和 URLAuthenticationChallenge
處理鼓拧。
這些身份驗(yàn)證 APIs 用于提示授權(quán)的服務(wù)器,而不是一般用于需要身份驗(yàn)證或等效的 header APIs越妈。
支持的身份驗(yàn)證方案:
HTTP Basic 身份驗(yàn)證
Request
的 authenticate
方法將在使用 URLAuthenticationChallenge
進(jìn)行質(zhì)詢時(shí)自動(dòng)提供 URLCredential
(如果適用):
let user = "user"
let password = "password"
AF.request("https://httpbin.org/basic-auth/\(user)/\(password)")
.authenticate(username: user, password: password)
.responseJSON { response in
debugPrint(response)
}
使用 URLCredential
進(jìn)行驗(yàn)證
let user = "user"
let password = "password"
let credential = URLCredential(user: user, password: password, persistence: .forSession)
AF.request("https://httpbin.org/basic-auth/\(user)/\(password)")
.authenticate(with: credential)
.responseJSON { response in
debugPrint(response)
}
需要注意的是季俩,當(dāng)使用
URLCredential
進(jìn)行身份驗(yàn)證時(shí),如果服務(wù)器發(fā)出質(zhì)詢梅掠,底層URLSession
實(shí)際上將發(fā)出兩個(gè)請(qǐng)求酌住。第一個(gè)請(qǐng)求將不包括“可能”觸發(fā)服務(wù)器質(zhì)詢的 credential。然后阎抒,Alamofire 接收質(zhì)詢赂韵,追加 credential,并由底層URLSession
重試請(qǐng)求挠蛉。
手動(dòng)驗(yàn)證
如果您正在與始終需要 Authenticate
或類似 header 而不提示的 API 通信祭示,則可以手動(dòng)添加:
let user = "user"
let password = "password"
let headers: HTTPHeaders = [.authorization(username: user, password: password)]
AF.request("https://httpbin.org/basic-auth/user/password", headers: headers)
.responseJSON { response in
debugPrint(response)
}
但是,必須是所有請(qǐng)求的一部分的 headers谴古,通常作為自定義 URLSessionConfiguration
的一部分或通過使用 RequestAdapter
來更好地處理质涛。
下載數(shù)據(jù)到文件中
除了將數(shù)據(jù)提取到內(nèi)存中之外,Alamofire 還提供了 Session.download
掰担、DownloadRequest
和 DownloadResponse<Success汇陆,F(xiàn)ailure:Error>
以方便下載數(shù)據(jù)到磁盤。雖然下載到內(nèi)存中對(duì)小負(fù)載(如大多數(shù) JSON API 響應(yīng))非常有用带饱,但獲取更大的資源(如圖像和視頻)應(yīng)下載到磁盤毡代,以避免應(yīng)用程序出現(xiàn)內(nèi)存問題。
AF.download("https://httpbin.org/image/png").responseData { response in
if let data = response.value {
let image = UIImage(data: data)
}
}
DownloadRequest
具有與DataRequest
相同的大多數(shù)響應(yīng) handlers勺疼。但是教寂,由于它將數(shù)據(jù)下載到磁盤,因此序列化響應(yīng)涉及從磁盤讀取执庐,還可能涉及將大量數(shù)據(jù)讀入內(nèi)存酪耕。在設(shè)計(jì)下載處理時(shí),記住這些事實(shí)是很重要的轨淌。
下載文件的存放位置
所有下載的數(shù)據(jù)最初都存儲(chǔ)在系統(tǒng)臨時(shí)目錄中迂烁。它最終會(huì)在將來的某個(gè)時(shí)候被系統(tǒng)刪除看尼,所以如果它需要更長(zhǎng)的壽命,將文件移到其他地方是很重要的盟步。
您可以提供 Destination
閉包藏斩,將文件從臨時(shí)目錄移動(dòng)到最終的存放位置。在臨時(shí)文件實(shí)際移動(dòng)到 destinationURL
之前却盘,將執(zhí)行閉包中指定的 Options
狰域。當(dāng)前支持的兩個(gè) Options
是:
-
.createIntermediateDirectories
- 如果指定,則為目標(biāo) URL 創(chuàng)建中間目錄谷炸。 -
.removePreviousFile
- 如果指定,則從目標(biāo) URL 中刪除以前的文件禀挫。
let destination: DownloadRequest.Destination = { _, _ in
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileURL = documentsURL.appendingPathComponent("image.png")
return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}
AF.download("https://httpbin.org/image/png", to: destination).response { response in
debugPrint(response)
if response.error == nil, let imagePath = response.fileURL?.path {
let image = UIImage(contentsOfFile: imagePath)
}
}
您還可以使用建議的 destination API:
let destination = DownloadRequest.suggestedDownloadDestination(for: .documentDirectory)
AF.download("https://httpbin.org/image/png", to: destination)
下載進(jìn)度
很多時(shí)候向用戶報(bào)告下載進(jìn)度是有幫助的旬陡。任何 DownloadRequest
都可以使用 downloadProgress
報(bào)告下載進(jìn)度。
AF.download("https://httpbin.org/image/png")
.downloadProgress { progress in
print("Download Progress: \(progress.fractionCompleted)")
}
.responseData { response in
if let data = response.value {
let image = UIImage(data: data)
}
}
URLSession
的進(jìn)度報(bào)告 APIs(也是 Alamofire 的)只有在服務(wù)器正確返回可用于計(jì)算進(jìn)度的 Content-Length
header 時(shí)才能工作语婴。如果沒有這個(gè) header描孟,進(jìn)度將保持在 0.0
,直到下載完成砰左,此時(shí)進(jìn)度將跳到 1.0
匿醒。
downloadProgress
API 還可以接收一個(gè) queue
參數(shù),該參數(shù)定義應(yīng)該對(duì)哪個(gè) DispatchQueue
調(diào)用下載進(jìn)度閉包缠导。
let utilityQueue = DispatchQueue.global(qos: .utility)
AF.download("https://httpbin.org/image/png")
.downloadProgress(queue: utilityQueue) { progress in
print("Download Progress: \(progress.fractionCompleted)")
}
.responseData { response in
if let data = response.value {
let image = UIImage(data: data)
}
}
取消和恢復(fù)下載
除了所有請(qǐng)求類都有 cancel()
API 外廉羔,DownloadRequest
還可以生成恢復(fù)數(shù)據(jù),這些數(shù)據(jù)可以用于以后恢復(fù)下載僻造。此 API 有兩種形式:1)cancel(producingResumeData: Bool)
憋他,它允許控制是否生成恢復(fù)數(shù)據(jù),但僅在 DownloadResponse
可用髓削;2)cancel(byProducingResumeData: (_ resumeData: Data?) -> Void)
竹挡,它執(zhí)行相同的操作,但恢復(fù)數(shù)據(jù)在 completion handler 中可用立膛。
如果 DownloadRequest
被取消或中斷揪罕,則底層的 URLSessionDownloadTask
可能會(huì)生成恢復(fù)數(shù)據(jù)。如果發(fā)生這種情況宝泵,可以重新使用恢復(fù)數(shù)據(jù)來重新啟動(dòng)停止的 DownloadRequest
好啰。
重要提示:在所有 Apple 平臺(tái)的某些版本(iOS 10 - 10.2、macOS 10.12 - 10.12.2儿奶、tvOS 10 - 10.1坎怪、watchOS 3 - 3.1.1)上,
resumeData
在后臺(tái)URLSessionConfiguration
上被破壞廓握。resumeData
生成邏輯中存在一個(gè)潛在的錯(cuò)誤搅窿,即數(shù)據(jù)寫入錯(cuò)誤嘁酿,并且總是無法恢復(fù)下載。有關(guān)此錯(cuò)誤和可能的解決方法的詳細(xì)信息男应,請(qǐng)參閱此 Stack Overflow 的帖子闹司。
var resumeData: Data!
let download = AF.download("https://httpbin.org/image/png").responseData { response in
if let data = response.value {
let image = UIImage(data: data)
}
}
// download.cancel(producingResumeData: true) // Makes resumeData available in response only.
download.cancel { data in
resumeData = data
}
AF.download(resumingWith: resumeData).responseData { response in
if let data = response.value {
let image = UIImage(data: data)
}
}
上傳數(shù)據(jù)到服務(wù)器
當(dāng)使用 JSON 或 URL 編碼的參數(shù)向服務(wù)器發(fā)送相對(duì)少量的數(shù)據(jù)時(shí),request()
通常就足夠了沐飘。如果需要從內(nèi)存游桩、文件 URL 或 InputStream 中的 Data
發(fā)送大量數(shù)據(jù),那么 upload()
就是您想要使用的耐朴。
上傳 Data
let data = Data("data".utf8)
AF.upload(data, to: "https://httpbin.org/post").responseJSON { response in
debugPrint(response)
}
上傳文件
let fileURL = Bundle.main.url(forResource: "video", withExtension: "mov")
AF.upload(fileURL, to: "https://httpbin.org/post").responseJSON { response in
debugPrint(response)
}
上傳多表單數(shù)據(jù)
AF.upload(multipartFormData: { multipartFormData in
multipartFormData.append(Data("one".utf8), withName: "one")
multipartFormData.append(Data("two".utf8), withName: "two")
}, to: "https://httpbin.org/post")
.responseJSON { response in
debugPrint(response)
}
上傳進(jìn)度
當(dāng)用戶等待上傳完成時(shí)借卧,有時(shí)向用戶顯示上傳的進(jìn)度會(huì)很方便。任何 UploadRequest 都可以使用 uploadProgress
和 downloadProgress
報(bào)告響應(yīng)數(shù)據(jù)下載的上傳進(jìn)度和下載進(jìn)度筛峭。
let fileURL = Bundle.main.url(forResource: "video", withExtension: "mov")
AF.upload(fileURL, to: "https://httpbin.org/post")
.uploadProgress { progress in
print("Upload Progress: \(progress.fractionCompleted)")
}
.downloadProgress { progress in
print("Download Progress: \(progress.fractionCompleted)")
}
.responseJSON { response in
debugPrint(response)
}
統(tǒng)計(jì)指標(biāo)
URLSessionTaskMetrics
Alamofire 為每個(gè) Request
收集了 URLSessionTaskMetrics
铐刘。URLSessionTaskMetrics
封裝了一些關(guān)于底層網(wǎng)絡(luò)連接、請(qǐng)求和響應(yīng)時(shí)間的奇妙統(tǒng)計(jì)信息影晓。
AF.request("https://httpbin.org/get").responseJSON { response in
print(response.metrics)
}
cURL 的命令輸出
調(diào)試平臺(tái)問題可能令人沮喪镰吵。幸運(yùn)的是,Alamofire 的 Request
類型可以生成等效的 cURL 命令挂签,以便調(diào)試疤祭。由于 Alamofire Request
創(chuàng)建的異步性,這個(gè) API 有同步和異步兩個(gè)版本饵婆。要盡快獲取 cURL 命令勺馆,可以將 cURLDescription
鏈接到請(qǐng)求上:
AF.request("https://httpbin.org/get")
.cURLDescription { description in
print(description)
}
.responseJSON { response in
debugPrint(response.metrics)
}
這將會(huì)生成:
$ curl -v \
-X GET \
-H "Accept-Language: en;q=1.0" \
-H "Accept-Encoding: br;q=1.0, gzip;q=0.9, deflate;q=0.8" \
-H "User-Agent: Demo/1.0 (com.demo.Demo; build:1; iOS 13.0.0) Alamofire/1.0" \
"https://httpbin.org/get"