【iOS開發(fā)】Alamofire框架的使用一 —— 基本用法

這篇文章是 Alamofire 5.0 以前的文檔喜庞,最新文檔請查看: Alamofire 5 的使用 - 基本用法


對于使用Objective-C的開發(fā)者偎行,一定非常熟悉AFNetworking這個網(wǎng)絡框架梆掸。在蘋果推出的Swift之后榕茧,AFNetworking的作者專門用Swift來編寫一個類似AFNetworking的網(wǎng)絡框架授帕,稱為Alamofire鸭栖。Alamofire地址 >>

我分兩篇文章介紹如何使用Alamofire框架。文章的內(nèi)容主要是翻譯Alamofire的readme有序。第二篇文章 >>

功能

  • 鏈式請求 / 響應方法
  • URL / JSON / plist參數(shù)編碼
  • 上傳文件 / 數(shù)據(jù) / 流 / 多表單數(shù)據(jù)
  • 使用請求或者斷點下載來下載文件
  • 使用URL憑據(jù)進行身份認證
  • HTTP響應驗證
  • 包含進度的上傳和下載閉包
  • cURL命令的輸出
  • 動態(tài)適配和重試請求
  • TLS證書和Public Key Pinning
  • 網(wǎng)絡可達性
  • 全面的單元和集成測試覆蓋率

組件庫

為了讓Alamofire專注于核心網(wǎng)絡的實現(xiàn)抹腿,Alamofire生態(tài)系統(tǒng)還有另外兩個庫:

  • AlamofireImage:一個圖片庫,包括圖像響應序列化器旭寿、UIImageUIImageView的擴展幢踏、自定義圖像濾鏡、內(nèi)存中自動清除和基于優(yōu)先級的圖像下載系統(tǒng)许师。
  • AlamofireNetworkActivityIndicator:控制iOS應用的網(wǎng)絡活動指示器。包含可配置的延遲計時器來幫助減少閃光僚匆,并且支持不受Alamofire管理的URLSession實例微渠。

要求的使用環(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 '項目名稱' do
    pod 'Alamofire', '~> 5.0.0-beta.5'
end

iOS版本和Alamofire版本可以自己根據(jù)實際情況自行更改。CocoaPods是比較常用的第三方庫管理工具咧擂,其他方法就不詳細說了逞盆。

如何使用

發(fā)請求

Alamofire.request("http://baidu.com/")

響應處理

直接在請求后面用點語法鏈接響應處理:

Alamofire.request("https://httpbin.org/get").responseJSON { response in
    print(response.request)  // 原始的URL請求
    print(response.response) // HTTP URL響應
    print(response.data)     // 服務器返回的數(shù)據(jù)
    print(response.result)   // 響應序列化結(jié)果,在這個閉包里松申,存儲的是JSON數(shù)據(jù)

    if let JSON = response.result.value {
        print("JSON: \(JSON)")
    }
}

在上面的例子中云芦,responseJSON handler直接拼接到請求后面,當請求完成后被調(diào)用贸桶。這個閉包一旦收到響應后舅逸,就會處理這個響應,并不會因為等待服務器的響應而造成阻塞執(zhí)行皇筛。請求的結(jié)果僅在響應閉包的范圍內(nèi)可用琉历。其他任何與服務器返回的響應或者數(shù)據(jù)相關的操作,都必須在這個閉包內(nèi)執(zhí)行。

Alamofire默認情況下包含五種不同的響應handler:

// 響應 Handler - 未序列化的響應
func response(
    queue: DispatchQueue?,
    completionHandler: @escaping (DefaultDataResponse) -> Void)
    -> Self

// 響應數(shù)據(jù) Handler - 序列化成數(shù)據(jù)類型
func responseData(
    queue: DispatchQueue?,
    completionHandler: @escaping (DataResponse<Data>) -> Void)
    -> Self

// 響應字符串 Handler - 序列化成字符串類型
func responseString(
    queue: DispatchQueue?,
    encoding: String.Encoding?,
    completionHandler: @escaping (DataResponse<String>) -> Void)
    -> Self

// 響應 JSON Handler - 序列化成Any類型
func responseJSON(
    queue: DispatchQueue?,
    completionHandler: @escaping (DataResponse<Any>) -> Void)
    -> Self

// 響應 PropertyList (plist) Handler - 序列化成Any類型
func responsePropertyList(
    queue: DispatchQueue?,
    completionHandler: @escaping (DataResponse<Any>) -> Void))
    -> Self

所有的響應handler都不會對響應進行驗證旗笔。也就是說響應狀態(tài)碼在400..<500500..<600范圍內(nèi)彪置,都不會觸發(fā)錯誤。

響應 Handler

response handler不處理任何響應數(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)")
    }
}

一般情況下不建議使用這種沒有響應序列化器的handler,而應該使用下面有特定序列化器的handler撮弧。

響應數(shù)據(jù) Handler

responseData handler使用responseDataSerializer(這個對象把服務器的數(shù)據(jù)序列化成其他類型)來提取服務器返回的數(shù)據(jù)潘懊。如果沒有返回錯誤并且有數(shù)據(jù)返回,那么響應Result將會是.success想虎,valueData類型卦尊。

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)")
    }
}
響應字符串 Handler

responseString handler使用responseStringSerializer對象根據(jù)指定的編碼格式把服務器返回的數(shù)據(jù)轉(zhuǎn)換成String。如果沒有返回錯誤并且服務器的數(shù)據(jù)成功地轉(zhuǎn)換為String舌厨,那么響應Result將會是.success岂却,valueString類型。

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

如果沒有指定編碼格式裙椭,將會使用服務器的HTTPURLResponse指定的格式躏哩。如果服務器無法確定編碼格式,那么默認使用.isoLatin1揉燃。

響應 JSON Handler

responseJSON handler使用responseJSONSerializer根據(jù)指定的JSONSerialization.ReadingOptions把服務器返回的數(shù)據(jù)轉(zhuǎn)換成Any類型扫尺。如果沒有返回錯誤并且服務器的數(shù)據(jù)成功地轉(zhuǎn)換為JSON對象,那么響應Result將會是.success炊汤,valueAny類型正驻。

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

    if let json = response.result.value {
        print("JSON: \(json)")
    }
}

所有JSON的序列化,都是使用JSONSerialization完成的抢腐。

鏈式響應handler

響應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)")
    }

注意:在同一個請求中使用多個響應handler姑曙,要求服務器的數(shù)據(jù)會被序列化多次,每次對應一個handler迈倍。

響應handler隊列

默認情況下伤靠,響應handler是在主隊列執(zhí)行的。但是我們也可以自定義隊列:

let utilityQueue = DispatchQueue.global(qos: .utility)

Alamofire.request("https://httpbin.org/get").responseJSON(queue: utilityQueue) { response in
    print("Executing response handler on utility queue")
}

響應驗證

默認情況下啼染,Alamofire把所有完成的請求當做是成功的請求宴合,無論響應的內(nèi)容是什么。如果響應有一個不能被接受的狀態(tài)碼或者MIME類型迹鹅,在響應handler之前調(diào)用validate將會產(chǎ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)
    }
}
自動驗證

自動驗證在200…299范圍內(nèi)的狀態(tài)碼;如果請求頭中有指定Accept斜棚,那么也會驗證響應頭的與請求頭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)
    }
}

響應緩存

響應緩存是使用系統(tǒng)的框架URLCache來處理的。它提供了內(nèi)存和磁盤上的緩存,并允許我們控制內(nèi)存和磁盤的大小脂新。

默認情況下挪捕,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ù):

Alamofire.request("https://httpbin.org/get") // 默認是get請求

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级零、JSONPropertyList。還支持遵循了ParameterEncoding協(xié)議的自定義編碼滞乙。

URL編碼

URLEncoding類型創(chuàng)建了一個URL編碼的查詢字符串來設置或者添加到一個現(xiàn)有的URL查詢字符串奏纪,或者設置URL請求的請求體。查詢字符串是否被設置或者添加到現(xiàn)有的URL查詢字符串斩启,或者被作為HTTP請求體序调,決定于編碼的Destination。編碼的Destination有三個case:

  • .methodDependent:為GET兔簇、HEADDELETE請求使用編碼查詢字符串來設置或者添加到現(xiàn)有查詢字符串发绢,并且使用其他HTTP方法來設置請求體。
  • .queryString:設置或者添加編碼查詢字符串到現(xiàn)有查詢字符串
  • .httpBody:把編碼查詢字符串作為URL請求的請求體

一個編碼請求的請求體的Content-Type字段被設置為application/x-www-form-urlencoded; charset=utf-8垄琐。因為沒有公開的標準說明如何編碼集合類型边酒,所以按照慣例在key后面添加[]來表示數(shù)組的值(foo[]=1&foo[]=2),在key外面包一個中括號來表示字典的值(foo[bar]=baz)狸窘。

使用URL編碼參數(shù)的GET請求
let parameters: Parameters = ["foo": "bar"]

// 下面這三種寫法是等價的
Alamofire.request("https://httpbin.org/get", parameters: parameters) // encoding 默認是`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請求
let parameters: Parameters = [
    "foo": "bar",
    "baz": ["a", 1],
    "qux": [
        "x": 1,
        "y": 2,
        "z": 3
    ]
]

// 下面這三種寫法是等價的
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
設置Bool類型參數(shù)的編碼

URLEncoding.BoolEncoding提供了兩種編碼方式:

  • .numeric:把true編碼為1墩朦,false編碼為0
  • .literal:把true編碼為truefalse編碼為false

默認情況下:Alamofire使用.numeric翻擒。

可以使用下面的初始化函數(shù)來創(chuàng)建URLEncoding氓涣,指定Bool編碼的類型:

let encoding = URLEncoding(boolEncoding: .literal)
設置Array類型參數(shù)編碼

URLEncoding.ArrayEncoding提供了兩種編碼方式:

  • .brackets: 在每個元素值的key后面加上一個[],如foo=[1,2]編碼成foo[]=1&foo[]=2
  • .noBrackets:不添加[]陋气,例如foo=[1,2]編碼成``foo=1&foo=2`

默認情況下春哨,Alamofire使用.brackets

可以使用下面的初始化函數(shù)來創(chuàng)建URLEncoding恩伺,指定Array編碼的類型:

let encoding = URLEncoding(arrayEncoding: .noBrackets)
JSON編碼

JSONEncoding類型創(chuàng)建了一個JOSN對象,并作為請求體椰拒。編碼請求的請求頭的Content-Type請求字段被設置為application/json晶渠。

使用JSON編碼參數(shù)的POST請求
let parameters: Parameters = [
    "foo": [1,2,3],
    "bar": [
        "baz": "qux"
    ]
]

// 下面這兩種寫法是等價的
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ù)關聯(lián)格式和寫選項值,使用PropertyListSerialization來創(chuàng)建一個屬性列表對象燃观,并作為請求體褒脯。編碼請求的請求頭的Content-Type請求字段被設置為application/x-plist

自定義編碼

如果提供的ParameterEncoding類型不能滿足我們的要求缆毁,可以創(chuàng)建自定義編碼番川。下面演示如何快速自定義一個JSONStringArrayEncoding類型把JSON字符串數(shù)組編碼到請求中。

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
    }
}
手動URL請求參數(shù)編碼

ParameterEncodingAPI可以在創(chuàng)建網(wǎ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請求頭

可以直接在請求方法添加自定義HTTP請求頭颁督,這有利于我們在請求中添加請求頭践啄。

let headers: HTTPHeaders = [
    "Authorization": "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
    "Accept": "application/json"
]

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

對于那些不變的請求頭,建議在URLSessionConfiguration設置沉御,這樣就可以自動被用于任何URLSession創(chuàng)建的URLSessionTask屿讽。

默認的Alamofire SessionManager為每一個請求提供了一個默認的請求頭集合,包括:

  • Accept-Encoding吠裆,默認是gzip;q=1.0, compress;q=0.5伐谈。
  • Accept-Language,默認是系統(tǒng)的前6個偏好語言试疙,格式類似于en;q=1.0诵棵。
  • User-Agent,包含當前應用程序的版本信息祝旷。例如iOS Example/1.0 (com.alamofire.iOS-Example; build:1; iOS 10.0.0) Alamofire/4.0.0履澳。

如果要自定義這些請求頭集合,我們必須創(chuàng)建一個自定義的URLSessionConfiguration缓屠,defaultHTTPHeaders屬性將會被更新奇昙,并且自定義的會話配置也會應用到新的SessionManager實例。

認證

認證是使用系統(tǒng)框架URLCredentialURLAuthenticationChallenge實現(xiàn)的敌完。

支持的認證方案
  • HTTP Basic
  • HTTP Digest
  • Kerberos
  • NTLM
HTTP Basic認證

在合適的時候储耐,在一個請求的authenticate方法會自動提供一個URLCredentialURLAuthenticationChallenge

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ù)服務器實現(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認證
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來做認證滨溉,如果服務器發(fā)出一個challenge什湘,底層的URLSession實際上最終會發(fā)兩次請求。第一次請求不會包含credential晦攒,并且可能會觸發(fā)服務器發(fā)出一個challenge闽撤。這個challenge會被Alamofire接收,credential會被添加脯颜,然后URLSessin會重試請求哟旗。

將數(shù)據(jù)下載到文件

Alamofire可以把服務器的數(shù)據(jù)下載到內(nèi)存(in-memory)或者硬盤(on-disk)中。所有Alamofire.requestAPI下載的數(shù)據(jù)都是存儲在內(nèi)存中栋操。這比較適合小文件闸餐,更高效;但是不適合大文件矾芙,因為大文件會把內(nèi)存耗盡舍沙。我們要使用Alamofire.downloadAPI把服務器的數(shù)據(jù)下載到硬盤中。

下面這個方法只適用于macOS剔宪。因為在其他平臺不允許在應用沙盒外訪問文件系統(tǒng)拂铡。下面會講到如何在其他平臺下載文件壹无。

Alamofire.download("https://httpbin.org/image/png").responseData { response in
    if let data = response.result.value {
        let image = UIImage(data: data)
    }
}
下載文件存儲位置

我們可以提供一個DownloadFileDestination閉包把臨時文件夾的文件移動到一個目標文件夾。在臨時文件真正移動到destinationURL之前感帅,閉包內(nèi)部指定的DownloadOptions將會被執(zhí)行斗锭。目前支持的DownloadOptions有下面兩個:

  • .createIntermediateDirectories:如果指定了目標URL,將會創(chuàng)建中間目錄留瞳。
  • .removePreviousFile:如果指定了目標URL拒迅,將會移除之前的文件
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)
    }
}

也可以直接使用建議的下載目標API:

let destination = DownloadRequest.suggestedDownloadDestination(directory: .documentDirectory)
Alamofire.download("https://httpbin.org/image/png", to: destination)
下載進度

所有的DownloadRequest都可以使用downloadProgressAPI來反饋下載進度。

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

downloadProgressAPI還可以接受一個queue參數(shù)來指定下載進度閉包在哪個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)
    }
}
恢復下載

如果一個DownloadRequest被取消或中斷璧微,底層的URL會話會生成一個恢復數(shù)據(jù)∮擦海恢復數(shù)據(jù)可以被重新利用并在中斷的位置繼續(xù)下載前硫。恢復數(shù)據(jù)可以通過下載響應訪問荧止,然后在重新開始請求的時候被利用屹电。

重要:在iOS 10 - 10.2, macOS 10.12 - 10.12.2, tvOS 10 - 10.1, watchOS 3 - 3.1.1中,resumeData會被后臺URL會話配置破壞跃巡。因為在resumeData的生成邏輯有一個底層的bug危号,不能恢復下載。具體情況可以到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ù)到服務器

使用JOSN或者URL編碼參數(shù)上傳一些小數(shù)據(jù)到服務器外莲,使用Alamofire.request API就已經(jīng)足夠了。如果需要發(fā)送很大的數(shù)據(jù)兔朦,需要使用Alamofire.upload API偷线。當我們需要在后臺上傳數(shù)據(jù)時,也可以使用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)
        }
    }
)
上傳進度

所有的UploadRequest都可以使用uploadProgressdownloadProgress APIs來反饋上傳和下載進度声邦。

let fileURL = Bundle.main.url(forResource: "video", withExtension: "mov")

Alamofire.upload(fileURL, to: "https://httpbin.org/post")
    .uploadProgress { progress in // 默認在主線程中執(zhí)行
        print("Upload Progress: \(progress.fractionCompleted)")
    }
    .downloadProgress { progress in // 默認在主線程中執(zhí)行
        print("Download Progress: \(progress.fractionCompleted)")
    }
    .responseJSON { response in
        debugPrint(response)
}

統(tǒng)計指標

時間表

Alamofire在一個請求周期內(nèi)收集時間,并創(chuàng)建一個Tineline對象摆舟,它是響應類型的一個屬性亥曹。

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

上面的Timeline信息包括:

  • Latency: 0.428 seconds (延遲)
  • Request Duration: 0.428 seconds (請求時間)
  • Serialization Duration: 0.001 seconds (序列化時間)
  • Total Duration: 0.429 seconds (總時間)
URL會話任務指標

在iOS和tvOS 10和macOS 10.12中,蘋果發(fā)布了新的URLSessionTaskMetrics APIs恨诱。這個任務指標封裝了關于請求和響應執(zhí)行的神奇統(tǒng)計信息媳瞪。這個API和Timeline非常相似,但是提供了很多Alamofire沒有提供的統(tǒng)計信息胡野。這些指標可以通過任何響應去訪問。

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

注意:這些API只能在iOS和tvOS 10和macOS 10.12中使用痕鳍。所以硫豆,根據(jù)部署目標龙巨,可能需要加入版本判斷:

Alamofire.request("https://httpbin.org/get").responseJSON { response in
    if #available(iOS 10.0. *) {
        print(response.metrics)
    }
}

cURL命令輸出

調(diào)試平臺問題很讓人厭煩。慶幸的是熊响,Alamofire的Request對象遵循了CustomStringConvertibleCustomDebugStringConvertible協(xié)議來提供一些非常有用的調(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開發(fā)】Alamofire框架的使用二 —— 高級用法 >>

如果想第一時間關注我的文章汗茄,可訪問我的個人博客網(wǎng)站:曾文志 - iOS開發(fā)者秸弛,專注Swift。洪碳,拉到最下面訂閱我的博客递览,并記得在郵件中確認訂閱。謝謝瞳腌!

歡迎加入我管理的Swift開發(fā)群:536353151绞铃。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市嫂侍,隨后出現(xiàn)的幾起案子儿捧,更是在濱河造成了極大的恐慌,老刑警劉巖挑宠,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件菲盾,死亡現(xiàn)場離奇詭異,居然都是意外死亡各淀,警方通過查閱死者的電腦和手機懒鉴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來揪阿,“玉大人疗我,你說我怎么就攤上這事∧衔妫” “怎么了吴裤?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長溺健。 經(jīng)常有香客問我麦牺,道長,這世上最難降的妖魔是什么鞭缭? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任剖膳,我火速辦了婚禮,結(jié)果婚禮上岭辣,老公的妹妹穿的比我還像新娘吱晒。我一直安慰自己,他們只是感情好沦童,可當我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布仑濒。 她就那樣靜靜地躺著叹话,像睡著了一般。 火紅的嫁衣襯著肌膚如雪墩瞳。 梳的紋絲不亂的頭發(fā)上驼壶,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天,我揣著相機與錄音喉酌,去河邊找鬼热凹。 笑死,一個胖子當著我的面吹牛泪电,可吹牛的內(nèi)容都是我干的般妙。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼歪架,長吁一口氣:“原來是場噩夢啊……” “哼股冗!你這毒婦竟也來了杰妓?” 一聲冷哼從身側(cè)響起角钩,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎阳仔,沒想到半個月后攒霹,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體怯疤,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年催束,在試婚紗的時候發(fā)現(xiàn)自己被綠了集峦。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡抠刺,死狀恐怖塔淤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情速妖,我是刑警寧澤高蜂,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站罕容,受9級特大地震影響备恤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜锦秒,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一露泊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧旅择,春花似錦惭笑、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽铺敌。三九已至,卻和暖如春屁擅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背产弹。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工派歌, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人痰哨。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓胶果,卻偏偏與公主長得像,于是被迫代替她去往敵國和親斤斧。 傳聞我的和親對象是個殘疾皇子早抠,可洞房花燭夜當晚...
    茶點故事閱讀 44,611評論 2 353

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