Alamofire 基本用法文檔翻譯

本文的目的是單純的翻譯一下Alamofire的基本用法的文檔券犁,原文是在github上:https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md 你看到這篇文章的時候芋类,可能官方文檔會有更新(本文5.1.0版本)虑乖。個人也是按照自己的理解進行翻譯粤剧,能力有限贿讹,翻譯的不好的地方苛聘,希望看到的朋友們給我指正出來养篓,感激不盡~

使用 Alamofire


介紹


Alamofire 提供了一個優(yōu)雅的和可組合的接口去進行 HTTP 網(wǎng)絡請求秃流。它是在 Apple 官方的 Foundation 框架所提供的 URL Loading System 之上建立起來的,并不是自己去實現(xiàn)了這些HTTP的請求相關的功能柳弄。 URL Loading System 的核心是 URLSessionURLSessionTask 的子類們舶胀。Alamofire 把包括他們在內(nèi)的許多 API 封裝成了更加易于使用的API(interface)并且提供了開發(fā) App(需要使用 HTTP 網(wǎng)絡請求的) 所需要的各種網(wǎng)絡方面的功能。但是碧注,很重要的一點是你要知道 Alamofire 的核心功能是來自哪里的(URL Loading System )嚣伐,因此,熟悉 Apple 的 URL Loading System是很重要的萍丐。歸根結底轩端,Alamofire 的網(wǎng)絡功能是受限制于 URL Loading System 的能力的,因此遵守 URL Loading System 的行為和準則是必須的逝变。

另外基茵,在 Alamofire (和 URL Loading System 的一般情況)下的網(wǎng)絡請求都是異步的奋构。異步編程可能使不熟悉這個概念的程序員感到沮喪,但是異步編程的 好處 是非常多的拱层。

旁注:AF命名空間和參考

以前版本的 Alamofire 的文檔都使用了 Alamofire.request() 這樣的示例來進行網(wǎng)絡請求弥臼。這個 API 使用了Alamofire 的前綴,但是事實上沒有這個前綴根灯,也是可以的径缅。在任何文件中 import Alamofire 都可以在文件中全局的使用 request 以及其他的方法(functions)。從 Alamofire 5 版本開始烙肺,這種使用方法被移除了纳猪,取而代之的是 AF 這個全局的對 Session.default的引用(AF = Session.default)。這樣做的好處是使得在每次使用 Alamofire 提供的便捷的功能的時候不會去污染 Alamofire 的全局命名空間茬高,也不用每次使用 Alamofire 的時候都寫一遍全局的 Session API兆旬。同樣的原因,Alamofire 拓展出來的類型們會使用 af 這種拓展名來把 Alamofire 提供的功能和其他拓展所提供的功能進行區(qū)分怎栽。

發(fā)一個網(wǎng)絡請求(Making Requests)


Alamofire 提供了多個便捷的方法去發(fā)送網(wǎng)絡請求丽猬。最簡單的例子,只需要提供一個可以轉(zhuǎn)換成(converted into)URLString 就可以了 (第一個版本):

AF.request("https://httpbin.org/get").response { response in
    debugPrint(response)
}

注:所有的例子都需要在文件中 import Alamofire

這個方法實際上是 Alamofire 的 Session 發(fā)送網(wǎng)絡請求的兩個頂層(top-level) API 中的一個熏瞄,他的完整定義是這樣的:

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)建了一個 DataRequest 脚祟。在創(chuàng)建每個請求(request)的時候允許他們有自己不同的組成元素(components),他們可以有不同的 method 或者 headers强饮。 還允許每個請求都可以有自己的攔截器(RequestInterceptor)和 參數(shù)編碼(Encodable)由桌。

除上面之外還有一些額外的方法可以讓你使用 Parameters 類型的字典做為參數(shù) ParameterEncoding作為參數(shù)類型進行網(wǎng)絡請求。不建議再這些 API 了邮丰,它們會在將來被 Alamofire 移除掉行您。

這個 API 的 第二個版本 要簡單的多:

open func request(_ urlRequest: URLRequestConvertible, 
                  interceptor: RequestInterceptor? = nil) -> DataRequest

這個方法可以為任何一個符合 Alamofire 的 URLRequestConvertible 協(xié)議的類型創(chuàng)建一個 DataRequest 。所有之前版本的參數(shù)(對比上一個完整的函數(shù)中的參數(shù))都被封裝進了這個 URLRequestConvertible 的參數(shù)里面剪廉,這樣就產(chǎn)生了一個非常強大的抽象娃循。這一點我們在 高級用法 的文檔中進行了討論。

HTTP 請求方式 (HTTP Methods)

HTTPMethod 這個類型列舉出了 RFC 7231 §4.3 中定義的 HTTP 請求方式們(HTTP Methods):

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
    }
}

它里面的這些值可以被作為方法參數(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)

需要記住的很重要的一點是斗蒋,不同的網(wǎng)絡請求方式(HTTP methods)會有不同的語義捌斧,還會根據(jù)服務器的不同要求進行相應的參數(shù)編碼。比如泉沾,URLSessionAlamofire 都不支持往 GET 的請求體里插入數(shù)據(jù)(passing body data)(那樣 是 POST 的做法)捞蚂,那樣做的話會報錯。

Alamofire 還提供了一個 URLRequest 的拓展跷究。里面的 httpMethod 屬性起到了橋接的作用姓迅,這個屬性把 URLRequest 中的 String 類型的 HTTP 請求方式(HTTP Methods) 轉(zhuǎn)變?yōu)?Alamofire的 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 不支持的請求方式色冀,你可以拓展 HTTPMethod 來添加你自己的請求方式:

extension HTTPMethod {
    static let custom = HTTPMethod(rawValue: "CUSTOM")
}

設置其他的 URLRequest 的屬性(Setting Other URLRequest Properties)

Alamofire 創(chuàng)建網(wǎng)絡請求的時候提供了最通用的自定義請求參數(shù)潭袱,但是柱嫌,有時候這些默認的參數(shù)并不能滿足需求。通過傳入各種參數(shù)的方式創(chuàng)建請求的時候屯换,可以使用 RequestModifier 閉包來修改這個正在創(chuàng)建的請求编丘。舉個例子,可以設置這個請求的超時時間(URLRequest 'S timeoutInterval)為5s彤悔,我們利用這個閉包(closure)來這樣修改:

AF.request("https://httpbin.org/get", requestModifier: { $0.timeoutInterval = 5 }).response(...)

RequestModifier 也可以使用尾隨閉包的語法形式:

AF.request("https://httpbin.org/get") { urlRequest in
    urlRequest.timeoutInterval = 5
    urlRequest.allowsConstrainedNetworkAccess = false
}
.response(...)

RequestModifier僅僅適用于通過 URL 和自定義參數(shù)創(chuàng)建請求的時候(譯者:上面第一個版本的請求)嘉抓,并不適用于直接從 URLRequestConvertible 里面的值們來提供參數(shù)值的那種創(chuàng)建請求的方式(譯者:上面第二個版本的請求),因為第二個版本封裝起來的那些值應該提供所有的請求所需的參數(shù)值晕窑。另外抑片,一旦但多數(shù)的網(wǎng)絡請求需要在創(chuàng)建的時候進行修改的話,那么還是推薦使用 URLRequestConvertible(譯者:第二種高級抽象出來的類型提供所有參數(shù)杨赤,在創(chuàng)建的時候就可以修改敞斋,第一種采用閉包,是創(chuàng)建完了之后對請求進行的配置)疾牲。你可以更多的在這里進行了解: Advanced Usage documentation植捎。

請求參數(shù)和參數(shù)編碼(Request Parameters and Parameter Encoders)

Alamofire 支持傳遞任何 Encodable 類型的數(shù)據(jù)作為網(wǎng)絡請求的參數(shù)。這些參數(shù)會被一個符合 ParmeterEncoder 協(xié)議(protocol)的類型處理之后進行傳遞阳柔,添加到網(wǎng)絡請求之中济锄,然后通過網(wǎng)絡發(fā)送出去。Alamofier 里面有兩個符合 ParameterEncoder 的類型霍转,他們分別是:JSONParameterEncoderURLEncodedFormParameterEncoder谴忧。這倆類型涵蓋了幾乎所有的現(xiàn)代網(wǎng)絡請求服務所需的參數(shù)編碼方式(XML的編碼方式就作為練習交給使用我們的老鐵們了)委造。

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)
}

第一種參數(shù)編碼方式(URLEncodedFormParameterEncoder)

URLEncodedFormParameterEncoder 類型的編碼器將值編碼成url編碼(url-encoded)用以設置或者添加到現(xiàn)有的URL查詢字符串中(譯者:GET)妇穴,或者設置為HTTP請求的請求體(HTTP body)(譯者:POST)。通過設置編碼器的 destination 來控制被編碼后字符串設置到哪里(譯者:URL中或者HTTP body中)。URLEncodedFormParameterEncoder.Destination 這個枚舉有以下幾個枚舉值:

  • .methodDependent - 把編碼過后的結果設置到現(xiàn)有的.get赞警,.head.delete 類型的請求的URL查詢字符串中笤虫,以及其他的HTTP請求類型的請求體之中(HTTP body)。
  • .queryString - 把編碼過后的字符串設置或者添加到網(wǎng)絡請求的這個URL查詢字符串中。
  • .httpBody - 把編碼過后的字符串設置到 HTTP 請求體中去(HTTP body)罚拟。

譯者:第一種是根據(jù)情況自動選擇,第二種是與 GET 相同類別的請求阻逮,第三種是與 POST 相同類型的請求

對于使用HTTP請求體(HTTP body)的網(wǎng)絡請求,如果HTTP頭(HTTP header)中的 Content-Type 沒有被設置過,那么就會被設置成 application/x-www-form-urlencoded; charset=utf-8

在 Alamofire 內(nèi)部的實現(xiàn)中与柑,URLEncodedFormParameterEncoder 使用了 URLEncodedFormEncoder 去真正的執(zhí)行把一個 Encodable 類型的數(shù)據(jù)編碼成URL編碼的字符串。這個編碼器可以用于各種類型的自定義的類型們脯倚,包括有 Array 使用 ArrayEncodingBool 使用 BoolEncoding乳丰,Data 使用 DataEncoding汞斧,Date使用 DateEncoding,編碼鍵(keys)使用 KeyEncoding庙睡, 空格(spaces)使用 SpaceEncoding雕擂。

URL編碼參數(shù)的GET請求(GET Request With URL-Encoded Parameters):
let parameters = ["foo": "bar"]

// All three of these calls are equivalent
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請求(POST Request With URL-Encoded Parameters):
let parameters: [String: [String]] = [
    "foo": ["bar"],
    "baz": ["a", "b"],
    "qux": ["x", "y", "z"]
]

// All three of these calls are equivalent
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"
配置編碼過后的值的排序

從 Swift 4.2 開始,Swift 的 Dictionary 類型所使用的哈希算法會在運行時會產(chǎn)生隨機的內(nèi)部排序,這就使得每次啟動應用程序可能會有所不同。這樣會導致編碼過后的參數(shù)順序有所改變瓷马,從而影響緩存和其他行為。URLEncodedFormEncoder將會對編碼過后的鍵值對(key-value pairs)進行排序。這將會為所有 Encodable 類型提供一個恒定的順序的輸出,他可能會與該類型實際的編碼順序不符(譯者:自己寫的參數(shù)的順序痕届,當然這個順序在 Dictionary是隨機的,和編碼過后被排序過得參數(shù)順序可能會不匹配)。你可以通過設置 alphabetizeKeyValuePairsfalse 來返回實際的參數(shù)順序申屹,盡管這樣做的話可能會擁有隨機的 Dictionary 順序。

你可以創(chuàng)建一個你自己的 URLEncodedFormParameterEncoder 在傳遞進來 URLEncodedFormEncoder 的初始化方法中指定你所期望的 alphabetizeKeyValuePairs 的值:

let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(alphabetizeKeyValuePairs: false))
配置 Array 參數(shù)的編碼(Configuring the Encoding of Array Parameters)

由于沒有發(fā)布過任何有關如何編碼集合類型的規(guī)范,Alamofire 依照慣例,將 []添加到數(shù)組名字的后面(foo[]=1&foo[]=2)瑞驱,對于嵌套字典, 則將被方括號包圍的key添加到的后面(foo[bar]=baz)。

URLEncodedFormEncoder.ArrayEncoding這個枚舉提供了以下的編碼 Array 參數(shù)的方式:

  • .brackets - 為每一個數(shù)組里面的值添加一個空的方括號的集合晒奕,這個也是默認的選項砰盐。
  • .noBrackets - 沒有添加方括號楞卡,按照原樣進行編碼霜运。

Alamofire 默認的使用 .brackets 去編碼 Array, 這樣的話 foo = [1, 2] 就會被編碼成 foo[]=1&foo[]=2蒋腮。

使用 .noBrackets 編碼的話就會把 foo=[1, 2] 編碼成 foo=1&foo=2

你可以創(chuàng)建你自己的 URLEncodedFormParameterEncoder藕各,然后在需要傳遞 URLEncodedFormEncoder 的初始化方法中指定 ArrayEncoding

let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(arrayEncoding: .noBrackets))
配置 Bool 類型參數(shù)的編碼方式(Configuring the Encoding of Bool Parameters)

URLEncodedFormEncoder.BoolEncoding 枚舉提供了以下幾種用于編碼 Bool 類型參數(shù)的方式:

  • .numeric - 把 true 編碼為 1激况,把 false 編碼為 0。這也是默認的方式蹬挤。
  • .literal - 把 truefalse 編碼為文字蹋嵌。

Alamofire 默認的使用 .numeric 的這種方式慧起。

你可以創(chuàng)建你自己的 URLEncodedFormParameterEncoder馅袁,然后在需要傳遞 URLEncodedFormEncoder 的初始化方法中指定 BoolEncoding

let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(boolEncoding: .numeric))
配置 Data 類型的參數(shù)編碼

DataEncoding 包括以下幾種編碼 Data 參數(shù)類型的方式:

  • .deferredToData - 使用 Data 的本地的 Encodable 支持削茁。
  • .base64 - 把 Data 編碼為 Base 64 編碼格式的字符串捍岳。這是默認的方式搀绣。
  • .custom((Data) -> throws -> String) - 使用提供的閉包(closure)進行編碼贸毕。

你可以創(chuàng)建你自己的 URLEncodedFormParameterEncoder,然后在需要傳遞 URLEncodedFormEncoder 的初始化方法中指定 DataEncoding

let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(dataEncoding: .base64))
配置 Date類型的參數(shù)編碼:

鑒于有大量的方法可以吧 Date 編碼稱為 StringDateEncoding 包括了以下這些方式來編碼 Date 參數(shù):

  • .deferredToDate - 使用 Date 的本地的 Encodable 支持卧晓,這是默認的方式桐愉。
  • .secondsSince1970 - 把 Date 編碼為從1970年1月1日的午夜開始到現(xiàn)在的秒數(shù)。
  • .millisecondsSince1970 - 把 Date 編碼為從1970年1月1日的午夜開始到現(xiàn)在的毫秒數(shù)亿眠。
  • .iso8610 - 根據(jù) ISO 8610 和 RFC3339 標準對 Date 進行編碼碎罚。
  • formatted(DateFormatter) - 使用給定的 DateFormatterDate 進行編碼憔购。
  • custom((Date) -> throws -> String) - 使用給定的閉包(closure)對 Date 進行編碼。

你可以創(chuàng)建你自己的 URLEncodedFormParameterEncoder先馆,然后在需要傳遞 URLEncodedFormEncoder 的初始化方法中指定 DateEncoding

let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(dateEncoding: .iso8601))
配置鍵的編碼方式(Configuring the Encoding of Coding Keys)

由于參數(shù)鍵的樣式多鐘多樣梅惯,KeyEncoding 提供以下方法去編碼使用駝峰命名法(lowerCamelCase)的鍵:

  • .useDefaultKeys - 使用每種類型指定的鍵。這個是默認的方式仿野。
  • .convertToSnakeCase - 把鍵編碼為下劃線(譯者:snake是蛇形)樣式:oneTwoThree 變成 one_two_three铣减。
  • .convertToKebabCase - 把鍵編碼為橫線(譯者:kebab是烤串)樣式:oneTwoThree 變成 one-two-three
  • .capitalized - 僅僅大寫第一個字母脚作,又叫做 UpperCamelCaseoneTwoThree 變成 OneTwoThree葫哗。
  • .uppercased - 大寫所有字母: oneTwoThree 變成 ONETWOTHREE
  • lowercased - 小寫所有字母: oneTwoThree 變成 onetwothree球涛。
  • .custom((String) -> String) - 使用給定的閉包(closure)進行編碼劣针。

你可以創(chuàng)建你自己的 URLEncodedFormParameterEncoder,然后在需要傳遞 URLEncodedFormEncoder 的初始化方法中指定 KeyEncoding

let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(keyEncoding: .convertToSnakeCase))
配置空格編碼(Configuring the Encoding of Spaces)

比較早的編碼器們使用 + 去編碼空格亿扁,許多服務器至今仍然希望使用這種編碼而不是使用現(xiàn)代的百分號編碼酿秸,因此 Alamofire 包涵了以下的方式對空格進行編碼:

  • .percentEscaped - 通過應用標磚的百分比轉(zhuǎn)碼對空格字符進行編碼。" " 被編碼成 "%20"魏烫。這個是默認的方式。
  • .plusReplaced - 把 " " 替換 + 肝箱。" " 被編碼成 +哄褒。

你可以創(chuàng)建你自己的 URLEncodedFormParameterEncoder,然后在需要傳遞 URLEncodedFormEncoder 的初始化方法中指定 SpaceEncoding

let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(spaceEncoding: .plusReplaced))

第二種編碼方式(JSONParameterEncoder)

JSONParameterEncoder 利用Swift的 JSONEncoderEncodable 類型的值(values)進行編碼煌张,然后把編碼后的結果設置到 URLRequesthttpBody 中呐赡。如果 HTTP header中的 Content-Type 沒有設置過的話,會被設置成 aplication/json骏融。

發(fā)送一個JSON編碼(JSON-Encoded)參數(shù)的網(wǎ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

你可以通過傳遞一個你配置好的 JSONEncoder 實例來自定義 JSONParameterEncoder 的行為:

let encoder = JSONEncoder()
encoder.dateEncoding = .iso8601
encoder.keyEncodingStrategy = .convertToSnakeCase
let parameterEncoder = JSONParameterEncoder(encoder: encoder)
手動為 URLRequest 參數(shù)進行編碼

ParameterEncoder 的API們也可以用于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頭(HTTP Headers)

Alamofire 擁有自己的的 HTTPHeaders 類型萌狂,他是一個保持原有順序并且不區(qū)分大小寫的 HTTP頭的鍵值對表示。 HTTPHeader 類型封裝了一個單獨的鍵值對并且為通用的HTTP頭(headers)提供了許多種靜態(tài)的值怀泊。

為一個 Request 添加一個自定義的 HTTPHeaders 就像給一個 request 的方法傳遞一個值那么簡單:

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ù)組來構建:

let headers: HTTPHeaders = [
    .authorization(username: "Username", password: "Password"),
    .accept("application/json")
]

AF.request("https://httpbin.org/headers", headers: headers).responseJSON { response in
    debugPrint(response)
}

對于 HTTP 頭來說茫藏,沒有變的是,推薦把他們設置到 URLSessionConfiguration霹琼,這樣他們就可以提供給任何通過基礎的 URLSession 所創(chuàng)建的 URLSessionTask 之中了务傲。更多的信息請看會話設置(Session Configurations)章節(jié)。

默認的Alamofire Session 為每個 Request 提供了默認的頭部信息(headers)集合枣申,他們是:

  • Accept-Encoding售葡,根據(jù)RFC 7230 §4.2.3,默認的值是 br;q=1.0, gzip;q=0.8, deflate;q=0.6忠藤。
  • Accept-Language挟伙,根據(jù)RFC 7231 §5.3.5,默認的是系統(tǒng)上首選的6種語言模孩,格式就像是:en;q=1.0漫试。
  • User-Agent,根據(jù)RFC 7231 §5.5.3妒潭,包含有當前應用的版本信息着帽,舉個例子:iOS Example/1.0 (com.alamofire.iOS-Example; build:1; iOS 13.0.0) Alamofire/5.0.0

如果你想自定義這些頭部信息(headers),你應該去創(chuàng)建一個自定義的 URLSessionConfiguration 祭芦,更新 defaultHTTPHeaders 屬性并將配置應用于新的 Session 實例筷笨。使用 URLSessionConfiguration.af.default 去自定義你的配置并且保留 Alamofire 默認的頭部信息(headers)。

響應驗證(Response Validation)

Alamofire 默認的會把所有已經(jīng)完成的網(wǎng)絡請求都視為是成功的龟劲,忽略服務端響應的內(nèi)容胃夏。如果響應的狀態(tài)碼(status code)或者 MIME類型不能被接受(unacceptable)(譯者:異常狀態(tài)碼或類型錯誤啥的)的話可以在響應處理(response handler)生成錯誤之前調(diào)用 validate() 函數(shù)。

自動校驗(Automatic Validation)

validate() 這個API自動驗證響應狀態(tài)碼的區(qū)間是 200..<300昌跌,如果提供了請求的 Accept頭的話仰禀,請求的 Accept頭的信息要與響應的 Content-Type 相匹配。

AF.request("https://httpbin.org/get").validate().responseJSON { response in
    debugPrint(response)
}
手動校驗(Manual Validatin)
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)
        }
    }

響應處理方式(Response Handling)

Alamofire 的 DataRequestDownloadRequest 都有相應的響應類型:DataResponse<Success, Failure: Error>DownloadResponse<Success, Failure: Error>蚕愤。他們倆都有著相同的組成:一個序列化類型和一個錯誤類型答恶。默認情況下,所有的響應值都會產(chǎn)生錯誤類型(即:DataResponse<Success, AFError>)萍诱。Alamofire 在公開的 API 中使用更簡單的 AFDataResponse<Success>AFDownloadResponse<Success>悬嗓,他們都包涵有 AFError 類型。 UploadRequestDataRequest 的子類裕坊,使用同樣的 DataResponse 類型包竹。

處理 Alamofire 的 DataRequest 或者 UploadRequest 所產(chǎn)生的 DataResponse 涉及到將諸如 responseJSON 這樣的響應處理 鏈接DataRequest

AF.request("https://httpbin.org/get").responseJSON { response in
    debugPrint(response)
}

在上面的例子中,responseJSON 處理程序被添加到了 DataRequest 上,在 DataRequest 完成的時候執(zhí)行一次處理程序周瞎。傳遞給處理程序的是一個閉包(closure)苗缩,這個閉包接收一個 AFDataResponse<Any> 類型的值,這個值是由響應屬性中的 JSONRespnseSerializer 所制造出來的声诸。

這個閉包(closure)作為回調(diào)(callback)被添加上去以便在接收到響應的時候去執(zhí)行處理酱讶,這樣做比阻塞執(zhí)行來等待服務器的響應要好的多。請求的結果僅僅在響應閉包(closure)的范圍內(nèi)有效双絮。任何取決于響應的或者從服務器接收的數(shù)據(jù)的處理都要在響應閉包(closure)中完成浴麻。

在 Alamofire 下的網(wǎng)絡請求都是異步的。異步編程可能使不熟悉這個概念的程序員感到沮喪囤攀,但是異步編程的 好處 是非常多的软免。

Alamofire 中包涵了6種不同的默認的數(shù)據(jù)響應處理程序,包括:

// Response Handler - Unserialized 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

沒有任何一個響應處理程序?qū)姆掌骰貋淼?HTTPURLResponse 執(zhí)行校驗焚挠。

舉個例子:響應的狀態(tài)碼在 400..<500500..<600 的范圍內(nèi)并不會自動觸發(fā)任何 Error膏萧。Alamofire 采用響應校驗(Response Validation)(上文提到的) 方式來實現(xiàn)校驗。(譯者:所以處理函數(shù)中并不做任何響應的校驗)

響應處理程序(Response Handler)

response 處理程序并不校驗任何響應數(shù)據(jù)蝌衔。他僅僅直接從 URLSessionDelegate 轉(zhuǎn)發(fā)所有的信息(infomation)榛泛。這就是 Alamofire 等效于使用 cURL 去執(zhí)行 Request

AF.request("https://httpbin.org/get").response { response in
    debugPrint("Response: \(response)")
}

我們強烈建議您去 ResponseResult 類型的其他的響應序列化工具(譯者:我的感覺是不建議直接用response噩斟,因為提供了6個方法曹锨,建議使用其他更具體好用的方法吧)。

響應 Data 數(shù)據(jù)的處理程序(Response Data Handler)

responseData 處理程序使用 DataResponseSerializer 去提取和校驗服務器返回的 Data 數(shù)據(jù)剃允。如果沒有錯誤發(fā)生并且返回了 Data 數(shù)據(jù)沛简。那么響應中的 Result 將會是 .success 的并且它里面的 value 會變成服務器返回的 Data 數(shù)據(jù)。

AF.request("https://httpbin.org/get").responseData { response in
    debugPrint("Response: \(response)")
}
響應字符數(shù)據(jù)的處理程序(Response String Handler)

responseString 處理程序使用了 StringResponseSerializer 去把服務器返回的 Data 數(shù)據(jù)轉(zhuǎn)換成指定編碼的 String 斥废。如果沒有錯誤產(chǎn)生并且服務端返回的 數(shù)據(jù)成功的被序列化為 String 椒楣,相應的結果將會是 .success 并且結果的 value值將會是 String 類型。

AF.request("https://httpbin.org/get").responseString { response in
    debugPrint("Response: \(response)")
}

如果沒有指定任何編碼方式牡肉,Alamofire 將會使用服務端返回的 HTTPURLResponse 中指定的文本編碼捧灰。如果無法根據(jù)服務器響應確定文本編碼,默認的是采用 .isoLatin1统锤。

響應JSON數(shù)據(jù)的處理程序(Response JSON Handler)

responseJSON 處理程序利用 JSONResponseSerializer 去將服務端返回的 Data 數(shù)據(jù)轉(zhuǎn)換成使用指定 JSONSerialization.ReadingOptionsAny 類型的數(shù)據(jù)毛俏。如果沒有錯誤發(fā)生并且成功的將服務端的數(shù)據(jù)序列化成了JSON對象(JSON object),那么響應 AFResult 將會是 .sucess 并且他的 value 將會是 Any 類型饲窿。

AF.request("https://httpbin.org/get").responseJSON { response in
    debugPrint("Response: \(response)")
}

respnseJSON 中使用的 JSON 序列化是使用的 Foundation 框架中的 JSONSerialization來完成處理的拧抖。

響應 Decodable 數(shù)據(jù)的處理程序(Response Decodable Handler)

responseDecodable 數(shù)據(jù)處理程序使用 DecodableResponseSerializer 去將服務端返回的 Data 數(shù)據(jù)轉(zhuǎn)換成傳參進來的使用指定 DataDecoder(一個可以從數(shù)據(jù)(Data)解碼的解碼器(Decoder)的協(xié)議) 的 Decodable類型數(shù)據(jù)。如果沒有錯誤產(chǎn)生并且成功的將服務端返回的數(shù)據(jù)解碼成了一個 Decodable 類型免绿,那么響應的 Result 將會是 .success 的并且 他的 value 將會是傳參進來的類型。

struct HTTPBinResponse: Decodable { let url: String }

AF.request("https://httpbin.org/get").responseDecodable(of: HTTPBinResponse.self) { response in
    debugPrint("Response: \(response)")
}
鏈式的響應處理程序(Chained Response Handlers)

響應處理程序也可以被串聯(lián)起來:

Alamofire.request("https://httpbin.org/get")
    .responseString { response in
        print("Response String: \(response.value)")
    }
    .responseJSON { response in
        print("Response JSON: \(response.value)")
    }

很重要的一點是擦盾,如果在同一個 Request使用多重響應處理程序的話嘲驾,需要服務器對返回數(shù)據(jù)多次序列化淌哟,每個響應處理程序處理一次。最好不要在同一個 Request 上使用多重響應處理程序辽故,特別是在生產(chǎn)環(huán)境中徒仓。這種做做僅僅應該在開發(fā)環(huán)境或者沒有其他的更好的選擇的情況下去做。

相應處理程序隊列(Response Handler Queue)

給響應處理程序傳遞的閉包(closures)默認是在 .main 隊列上執(zhí)行的誊垢,但是也可以指定一個 DispatchQueue 給需要執(zhí)行的閉包(closure)掉弛。事實上,序列化的工作(把 Data 數(shù)據(jù)轉(zhuǎn)換成其他類型的數(shù)據(jù))總是在一個后臺隊列(background queue)中執(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)
}

響應的緩存(Response Caching)

響應緩存是由 URLCache 在系統(tǒng)級別進行處理的殃饿。他提供了一個內(nèi)存(in-memory)和磁盤(on-disk)緩存的結合體,使您可以操控內(nèi)存(in-memory)和磁盤(on-disk)部分的大小芋肠。

Alamofire默認的使用 URLCache.shared 這個實例進行自定義的 URLCache 實例的使用乎芳,請看會話配置(Session Configuration)章節(jié)。

認證方式(Authentication)

認證方式(Authentication)是由 URLCredentialURLAuthenticationChallenge 在系統(tǒng)框架級別進行操作的帖池。

這些認證方式的 API 適用于提示授權的服務器奈惑,并非一般用于需要 Authenticate(譯者:身份驗證) 和 等效的請求頭的服務器。

支持的驗證方式(Supported Authentication Schemes)
HTTP Basic 授權方式(HTTP Basic Authentication)

當使用 URLAuthenticationChallenge 質(zhì)詢的時候睡汹,在適當?shù)臅r候Request上的 authenticate方法會自動提供 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 授權方式(Authentication with URLCredential
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)
    }

需要注意的很重要的一點是肴甸,當你使用 URLCredential 來進行認證的時候,如果服務器發(fā)出了質(zhì)詢囚巴,那么底層的 URLSession 實際上最終會發(fā)出兩個請求(requests)原在。第一個請求將會不包括可能觸發(fā)服務器質(zhì)詢的憑據(jù)(credential)。Alamofire來接收這個服務器的質(zhì)詢文兢,并把憑證(credential)加上去晤斩,再由底層的的 URLSession 重新發(fā)送請求(request)。

手動授權(Manual Authentication)

如果你要與一個始終需要 Authenticate 或者類似的請求頭的 API 通信而沒有提示的時候姆坚,你可以手動的加上:

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)
    }

但是澳泵,必須作為所有請求的一部分的請求頭(headers)通常更好的被處理為自定義的 URLSessionConfiguration 的一部分,或者使用一個 RequestAdapter兼呵。(譯者注:這些在高級用法中有提到)

下載數(shù)據(jù)到文件(Downloading Data to a File)

除了把獲取到的數(shù)據(jù)放入內(nèi)存兔辅,Alamofire 還提供了 Session.downloadDownloadRequest击喂,和 DownloadResponse<Success, Failure: Error> 這些 API 去方便的下載數(shù)據(jù)到磁盤维苔。下載到內(nèi)存的行為非常適合像大多數(shù) JSON API 的很小的響應信息,而獲取大的資源像是圖片和視頻懂昂,就應該下載到磁盤以緩解應用的內(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ù)的響應處理程序(response handlers)。但是,由于他把數(shù)據(jù)下載到磁盤沸柔,序列化響應會涉及到從磁盤讀取數(shù)據(jù)循衰,這樣也可能導致往內(nèi)存中讀取了大量的數(shù)據(jù)。在涉設計你自己的數(shù)據(jù)下載處理的時候要記住這些褐澎,這很重要会钝。

文件下載的位置(Download File Destination)

所有下載的數(shù)據(jù)最初都存放在系統(tǒng)的臨時目錄里(temporary directory)。這些數(shù)據(jù)最終將會被系統(tǒng)在某個時間點刪除掉工三,因此如果有些情況需要這些文件存在的時間更長一些的話迁酸,把他們移動到某個其他的地方是很重要的。

你可以提供一個 Destination 閉包(closure)去把文件從臨時文件目錄移動到最終的位置上去俭正。在臨時文件被真正的移動到 destinationURL 之前奸鬓,閉包(closure)中指定的 Options 會被執(zhí)行,目前支持的兩種 Options 是:

  • . createIntermediateDirectories - 如果指定目錄段审,則為目標目錄創(chuàng)建中間目錄全蝶。
  • .removePreviousFile - 如果指定,則從目標目錄中刪除之前的文件寺枉。
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)
    }
}

你也可以使用建議的下載位置的 API:

let destination = DownloadRequest.suggestedDownloadDestination(for: .documentDirectory)

AF.download("https://httpbin.org/image/png", to: destination)
下載進度(download Progress)

很多時候抑淫,給用戶報告下載進度是很有用的。任何的 DownloadRequest 都可以利用 downloadProgress API 來報告下載進度姥闪。

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)
        }
    }

僅當服務器返回 Content-Length 頭的時候始苇, URLSession 的進度報告相關的 API 才起作用,對于 ALamofire 也是這樣的筐喳。沒有這個頭(header)的話催式,進度將停留在 0.0,下載完成避归,進度會跳到 1.0荣月。

downloadProgress API 也可以傳入一個 queue 參數(shù)來定義哪一個 DispatchQueue 是下載進度閉包(closure)所調(diào)用的的所在。

let progressQueue = DispatchQueue(label: "com.alamofire.progressQueue", qos: .utility)

AF.download("https://httpbin.org/image/png")
    .downloadProgress(queue: progressQueue) { progress in
        print("Download Progress: \(progress.fractionCompleted)")
    }
    .responseData { response in
        if let data = response.value {
            let image = UIImage(data: data)
        }
    }
取消和重新下載(Canceling and Resuming a Download)

除了所有的 Request 類都有 cancel() API 之外梳毙,DownloadRequest 還可以生成恢復數(shù)據(jù)(resume data)哺窄,這些恢復數(shù)據(jù)可以被用來在稍后的時候重新下載數(shù)據(jù)。這個 API 有兩種形式:cancel(producingResumeData: Bool) 可以控制是否生成恢復數(shù)據(jù)账锹,但是他僅僅對 DownloadResponse 有用萌业;cancel(byProducingResumeData:(_ resumeData: Data?) -> Void) 有著相同的作用,但是他可以讓恢復數(shù)據(jù)在完成處理程序(completion handler)中可用奸柬。

如果一個 DownloadRequest 被取消了或者被中斷了生年,底層的 URLSessinDownloadTask 可能會生成回復數(shù)據(jù)。如果生成了恢復數(shù)據(jù)廓奕,這個恢復數(shù)據(jù)可以再次被用來在中斷的地方重新開始 DownloadRequest抱婉。

極其重要: 在蘋果平臺的某些版本(iOS10 - 10.2档叔,macOS10.12-10.12.2, tvOS10-10.1蒸绩,watchOS3-3.1.1 )中蹲蒲,resumeData 在后臺的 URLSessionConfiguration 中是損壞的。在底層的 resumeData 生成邏輯中存在錯誤侵贵,錯誤的數(shù)據(jù)被寫入,導致了始終無法通過 resumeData 重新進行下載缘薛。關于更多的這個bug的信息和可能的解決辦法窍育,請看 Stack Overflow post

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ù)到服務器(Uploading Data to a Server)

當發(fā)送相對少量的使用 JSON 或者 URL 編碼的參數(shù)到服務器的時候宴胧,request() APIs 通常就夠用了漱抓。如果你需要發(fā)送從內(nèi)存中、通過文件 URL恕齐、或者 InputStream來的大量的 Data 的時候乞娄,upload() APIs 可能是你想要使用的。

上傳數(shù)據(jù)(Uploading Data)
let data = Data("data".utf8)

AF.upload(data, to: "https://httpbin.org/post").responseDecodable(of: HTTPBinResponse.self) { response in
    debugPrint(response)
}
上傳文件(Uploading a File)
let fileURL = Bundle.main.url(forResource: "video", withExtension: "mov")

AF.upload(fileURL, to: "https://httpbin.org/post").responseDecodable(of: HTTPBinResponse.self) { response in
    debugPrint(response)
}
上傳多部分表格數(shù)據(jù)(Uploading Multipart Form Data)
AF.upload(multipartFormData: { multipartFormData in
    multipartFormData.append(Data("one".utf8), withName: "one")
    multipartFormData.append(Data("two".utf8), withName: "two")
}, to: "https://httpbin.org/post")
    .responseDecodable(of: HTTPBinResponse.self) { response in
        debugPrint(response)
    }
上傳進度(Upload Progress)

當你的用戶正在等待他們上傳數(shù)據(jù)完成的時候显歧,有時候展示給用戶上傳進度對于他們是很便利的仪或。任何的 UploadRequest 使用 uploadProgressdownloadProgress的時候都可以報告上傳數(shù)據(jù)的進度和響應數(shù)據(jù)下載的進度。

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)")
    }
    .responseDecodable(of: HTTPBinResponse.self) { response in
        debugPrint(response)
    }

從服務器來的流式傳輸數(shù)據(jù)(Streaming Data from a Server)

使用流傳輸而不是積累不斷接收到的數(shù)據(jù)士骤,可以更好的服務于長時間下載大型的文件或者連接服務器范删。Alamofire 提供了 DataStreamRequest 這個類型以及相關的 API 去處理這種用法。盡管這樣就提供了好多像其他的 Request 一樣的 API拷肌,但是它仍然有幾個主要的區(qū)別到旦。最明顯的, DataStreamRequest 不在內(nèi)存中積累數(shù)據(jù)或者將數(shù)據(jù)存入磁盤(disk)巨缘。相反的是添忘,它在數(shù)據(jù)到達的時候反復調(diào)用 responseStream 閉包(closures)。在連接完成或者收到錯誤的時候會再次調(diào)用相同的閉包若锁。

每個 Handler 閉包(closure)都捕獲一個 Stream 值搁骑,其中包括一個 Event 以及可以取消請求的 CancellationToken

public struct Stream<Success, Failure: Error> {
    /// Latest `Event` from the stream.
    public let event: Event<Success, Failure>
    /// Token used to cancel the stream.
    public let token: CancellationToken
    /// Cancel the ongoing stream by canceling the underlying `DataStreamRequest`.
    public func cancel() {
        token.cancel()
    }
}

一個 Event`` 就是一個代表兩種流狀態(tài)的enum```拴清。

public enum Event<Success, Failure: Error> {
    /// Output produced every time the instance receives additional `Data`. The associated value contains the
    /// `Result` of processing the incoming `Data`.
    case stream(Result<Success, Failure>)
    /// Output produced when the instance has completed, whether due to stream end, cancellation, or an error.
    /// Associated `Completion` value contains the final state.
    case complete(Completion)
}

完成的時候靶病,Completion 值將會包含流結束時候的 DataStreamRequest 的狀態(tài)。

public struct Completion {
    /// Last `URLRequest` issued by the instance.
    public let request: URLRequest?
    /// Last `HTTPURLResponse` received by the instance.
    public let response: HTTPURLResponse?
    /// Last `URLSessionTaskMetrics` produced for the instance.
    public let metrics: URLSessionTaskMetrics?
    /// `AFError` produced for the instance, if any.
    public let error: AFError?
}
流數(shù)據(jù)(Streaming Data

可以想其他的 Alamofire 請求一樣去完成從服務器請求流數(shù)據(jù)口予,但是多了一個 Handler 閉包(closure)娄周。

func responseStream(on queue: DispatchQueue = .main, stream: @escaping Handler<Data, Never>) -> Self

提供的 queue 將會是 handler 閉包所在的位置。

AF.streamRequest(...).responseStream { stream in
    switch stream.event {
    case let .stream(result):
        switch result {
        case let .success(data):
            print(data)
        }
    case let .complete(completion):
        print(completion)
    }
}

在上面的例子中處理 .failure 這種 Result 的情況是不必要的沪停,接收 Data 不可能是失敗的煤辨。

流式的字符串(Streaming String s)

像流式 Data 一樣裳涛,String 可以添加 Handler 來處理流式字符串數(shù)據(jù)。

func responseStreamString(on queue: DispatchQueue = .main,
                          stream: @escaping StreamHandler<String, Never>) -> Self

String 值被解碼為 UTF8众辨,這種解碼不會失敗端三。

AF.streamRequest(...).responseStreamString { stream in
    switch stream.event {
    case let .stream(result):
        switch result {
        case let .success(string):
            print(string)
        }
    case let .complete(completion):
        print(completion)
    }
}
流式 Decodable 值(Streaming Decodable 值)

傳入的流式 Data 值可以使用 responseStreamDecodable 轉(zhuǎn)換成任意的 Decodable

func responseStreamDecodable<T: Decodable>(of type: T.Type = T.self,
                                           on queue: DispatchQueue = .main,
                                           using decoder: DataDecoder = JSONDecoder(),
                                           preprocessor: DataPreprocessor = PassthroughPreprocessor(),
                                           stream: @escaping Handler<T, AFError>) -> Self

解碼(Decoding)失敗并不會終止流鹃彻,而是在 ResultOutput 中生成一個 AFError郊闯。

AF.streamRequest(...).responseStreamDecodable(of: SomeType.self) { stream in
    switch stream.event {
    case let .stream(result):
        switch result {
        case let .success(value):
            print(value)
        case let .failure(error):
            print(error)
        }
    case let .complete(completion):
        print(completion)
    }
}
生成一個輸入流(Producing an InputStream)

除了使用 StreamHandler 閉包處理傳入的 Data 之外, DataStreamRequest 可以生成一個可以讀取到達的字節(jié)的 InputStream蛛株。

func asInputStream(bufferSize: Int = 1024) -> InputStream

通過這種方式創(chuàng)建的 InputStream 必須在讀取開始之前調(diào)用 open()团赁,或者將其傳遞給自動打開流的 API。一旦從這個方法返回了 InputStream谨履,這個方法的調(diào)用者有責任去保持 InputStream 存活欢摄,并且在讀取完之后調(diào)用 close()

let inputStream = AF.streamRequest(...)
    .responseStream { output in
        ...
    }
    .asInputStream()
取消(Cancellation)

DataStreamRequest 們可以用4種方式來取消笋粟。第一種怀挠,像所有其他的 Alamofire 的 Request 一樣,DataStreamRequest 可以調(diào)用 cancel() 來取消底層(underlying)的任務(task)并且完成流害捕。

let request = AF.streamRequest(...).responseStream(...)
...
request.cancel()

第二種绿淋,DataStreamRequest 們可以在 DataStreamSerializer 發(fā)生錯誤的時候被自動的取消。這種行為默認是被禁止的但是可以通過 automaticallyCancelOnStreamError 這個參數(shù)在創(chuàng)建請求的時候允許這種行為吨艇。

AF.streamRequest(..., automaticallyCancelOnStreamError: true).responseStream(...)

第三種躬它,DataStreamRequest 們將會在 Handler 閉包(closure)拋出錯誤的時候被取消。之后东涡,這個錯誤會被存入請求之中并且在 Completion 值中可用冯吓。

AF.streamRequest(...).responseStream { stream in
    // Process stream.
    throw SomeError() // Cancels request.
}

最后一種,DataStreamRequest 們可以使用 Stream 值的 cancel() 方法來取消疮跑。

AF.streamRequest(...).responseStream { stream in 
    // Decide to cancel request.
    stream.cancel()
}

統(tǒng)計指標(Statistical Metrics)

URLSessionTaskMetrics

Akanifure 為每一個 Request 收集 URLSessionTaskMetrics组贺。URLSessionTaskMetrics 封裝了一些關于基礎的網(wǎng)絡連接和請求響應事件的奇妙的統(tǒng)計信息。

AF.request("https://httpbin.org/get").responseJSON { response in
    print(response.metrics)
}

由于 FB7624529祖娘,目前收集 watchOS 的 URLSessionTaskMetrics 是不可用的失尖。

cURL 命令輸出(cURL Command Output)

調(diào)試平臺的錯誤可能讓人蛋疼,但是謝天謝地渐苏,Alamofire 的 Request 類型可以產(chǎn)生等效的 cURL 命令來方便調(diào)試掀潮。由于 Alamofire 的 Request 創(chuàng)建是異步的這個特性,這個API有同步(synchronous)的和一步的(asynchronous)兩個版本琼富。為了盡快的獲取 cURL 命令仪吧,你可以鏈接一個 CURLDescription 在request上:

AF.request("https://httpbin.org/get")
    .cURLDescription { description in
        print(description)
    }
    .responseJSON { response in
        debugPrint(response.metrics)
    }

這個就制造出來了:

$ 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"
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市鞠眉,隨后出現(xiàn)的幾起案子薯鼠,更是在濱河造成了極大的恐慌择诈,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,084評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件出皇,死亡現(xiàn)場離奇詭異羞芍,居然都是意外死亡,警方通過查閱死者的電腦和手機郊艘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評論 3 392
  • 文/潘曉璐 我一進店門荷科,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人纱注,你說我怎么就攤上這事步做。” “怎么了奈附?”我有些...
    開封第一講書人閱讀 163,450評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長煮剧。 經(jīng)常有香客問我斥滤,道長,這世上最難降的妖魔是什么勉盅? 我笑而不...
    開封第一講書人閱讀 58,322評論 1 293
  • 正文 為了忘掉前任佑颇,我火速辦了婚禮,結果婚禮上草娜,老公的妹妹穿的比我還像新娘挑胸。我一直安慰自己,他們只是感情好宰闰,可當我...
    茶點故事閱讀 67,370評論 6 390
  • 文/花漫 我一把揭開白布茬贵。 她就那樣靜靜地躺著,像睡著了一般移袍。 火紅的嫁衣襯著肌膚如雪解藻。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,274評論 1 300
  • 那天葡盗,我揣著相機與錄音螟左,去河邊找鬼。 笑死觅够,一個胖子當著我的面吹牛胶背,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播喘先,決...
    沈念sama閱讀 40,126評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼钳吟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了苹祟?” 一聲冷哼從身側(cè)響起砸抛,我...
    開封第一講書人閱讀 38,980評論 0 275
  • 序言:老撾萬榮一對情侶失蹤评雌,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后直焙,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體景东,經(jīng)...
    沈念sama閱讀 45,414評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,599評論 3 334
  • 正文 我和宋清朗相戀三年奔誓,在試婚紗的時候發(fā)現(xiàn)自己被綠了斤吐。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,773評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡厨喂,死狀恐怖和措,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蜕煌,我是刑警寧澤派阱,帶...
    沈念sama閱讀 35,470評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站斜纪,受9級特大地震影響贫母,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜盒刚,卻給世界環(huán)境...
    茶點故事閱讀 41,080評論 3 327
  • 文/蒙蒙 一腺劣、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧因块,春花似錦橘原、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至吩愧,卻和暖如春歼冰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背耻警。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評論 1 269
  • 我被黑心中介騙來泰國打工隔嫡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人甘穿。 一個月前我還...
    沈念sama閱讀 47,865評論 2 370
  • 正文 我出身青樓腮恩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親温兼。 傳聞我的和親對象是個殘疾皇子秸滴,可洞房花燭夜當晚...
    茶點故事閱讀 44,689評論 2 354

推薦閱讀更多精彩內(nèi)容