向大家介紹Alamofire的基本用法魂角,即如何通過iOS客戶端發(fā)起各種HTTP請求剩胁,并以Alamofire提供的不同方式處理HTTP response截粗。為了簡單起見,在我們的例子里蟋定,Alamofire的應(yīng)用都不會和UI代碼相關(guān),我們只演示HTTP通信的部分垢夹。首先溢吻,我們從一個最簡單的HTTP請求開始维费。
發(fā)起一個最基本的HTTP請求
打開AlamoFireDemo的ViewController.swift文件果元,添加下面的代碼:
import UIKit
import Alamofire
"引入"了Alamofire module之后,我們就可以使用它提供的各種API了犀盟。為了測試各種HTTP action而晒,我們需要一個與之配合的服務(wù)端程序。幸運的是阅畴,至少現(xiàn)在我們還不用專門去寫它倡怎,httpbin.org為我們提供了方便的HTTP測試API:
包括GET一個頁面、一個JSON贱枣、各種REST方法监署、常用的HTTP status、認證纽哥、下載等钠乏。它們可以很方便的讓我們來測試Alamofire提供的各種功能。我們從GET /ip開始春塌,httpbin會返回一個類似下面這樣的JSON:
{
"origin": "106.185.45.242"
}
回到Xcode晓避,在viewDidLoad()里,添加下面的代碼:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Alamofire.request(.GET, "https://httpbin.org/ip")
}
這樣只壳,我們就完成了一次對httpbin.org的GET請求俏拱。看似簡單吼句,卻有三點需要說明:
第一點是Alamofire.request這樣的用法锅必,該如何理解呢?Alamofire是類名嗎惕艳?request是一個class method嗎搞隐?實際上不是這樣的。前面的Alamofire是因為我們一開始引入了Alamofire module之后尔艇,開啟的名字空間尔许;而request則是這個名字空間里的一個函數(shù),我們可以在Alamofire的源代碼里找到它终娃。
第二點是request的第一個參數(shù)味廊,表示我們采用的HTTP方法。它是Alamofire定義的一個enum,在這里:
public enum Method: String {
case OPTIONS, GET, HEAD, POST, PUT, PATCH, DELETE, TRACE, CONNECT
}
第三點是Alamofire發(fā)起的HTTP request是和App主線程并行執(zhí)行的余佛,它并不會阻塞App的UI柠新。
理解了這三點,上面的代碼就沒有任何問題了辉巡。接下來恨憎,我們來看如何處理HTTP response。
處理HTTP response
Alamofire有一個最原始的獲取HTTP response的方法郊楣,像這樣:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Alamofire.request(.GET, "https://httpbin.org/ip")
.response { request, response, data, error in
print(request)
print(response)
print(data)
print(error)
}
}
從圖中的結(jié)果就可以看到closure的四個參數(shù)對應(yīng)的類型了憔恳,它們分別代表request/response的基本信息,返回內(nèi)容的原始數(shù)據(jù)以及錯誤信息净蚤。事實上钥组,如果我們查看response的實現(xiàn),就會發(fā)現(xiàn)更多內(nèi)容:
public func response(
queue queue: dispatch_queue_t? = nil,
completionHandler:
(NSURLRequest?,
NSHTTPURLResponse?,
NSData?,
NSError?) -> Void)
-> Self
{
delegate.queue.addOperationWithBlock {
dispatch_async(queue ?? dispatch_get_main_queue()) {
completionHandler(self.request,
self.response,
self.delegate.data,
self.delegate.error)
}
}
return self
}
response實際上有2個參數(shù)今瀑,只不過第一個參數(shù)有默認值程梦,所以,我們使用了一個trailing closure定義了它的第二個參數(shù)橘荠。當(dāng)?shù)谝粋€參數(shù)queue為nil時屿附,Alamofire默認會在App的主線程中執(zhí)行我們的handler,方便我們在收到HTTP請求的時候更新UI哥童。
當(dāng)我們通過這種方式得到了一個表示HTTP response的NSData對象后挺份,我們就可以把它變成Image、序列化成JSON等等如蚜。但是如果真是這樣压恒,我們就完全沒必要使用Alamofire了,因為這幾乎和NSURLSession的"套路"是一樣的错邦。實際上探赫,我們很少直接使用這個原始的response,Alamofire為我們提供了更方便的序列化結(jié)果的方法:
responseData(/*completionHandler*/)
responseString(encoding: NSStringEncoding,
/*completionHandler*/)
responseJSON(options: NSJSONReadingOptions,
/*completionHandler*/)
responsePropertyList(options: NSPropertyListReadOptions,
/*completionHandler*/)
它們之中撬呢,后三個方法的第一個參數(shù)可以先暫時忽略伦吠,因為它們都是有默認值的。這四個方法的共同之處就是它們有一個共同的handler魂拦,這個handler接受一個類型為Response<nsdata, nserror="" style="box-sizing: border-box;">的參數(shù)毛仪,用來讓我們在Alamofire完成序列化之后,做進一步的處理芯勘。</nsdata,>
而我們查看Response的源代碼就會發(fā)現(xiàn)箱靴,它封裝了之前我們用過的request / response / data:
public struct Response {
/// The URL request sent to the server.
public let request: NSURLRequest?
/// The server's response to the URL request.
public let response: NSHTTPURLResponse?
/// The data returned by the server.
public let data: NSData?
/// The result of response serialization.
public let result: Result
// Omit for simplicity...
}
并且,它還額外提供了一個Result<value, error="" style="box-sizing: border-box;">類型的屬性result荷愕,方便我們讀取序列化過之后的結(jié)果(簡單起見衡怀,例子中省略了實現(xiàn)的部分):</value,>
public enum Result {
case Success(Value)
case Failure(Error)
public var isSuccess: Bool {
// Omit for simplicity...
}
public var isFailure: Bool {
// Omit for simplicity...
}
public var value: Value? {
// Omit for simplicity...
}
public var error: Error? {
// Omit for simplicity...
}
}
另外棍矛,Result還實現(xiàn)了CustomStringConvertible protocol,我們可以直接print一個Result對象抛杨。
在理解了這些處理HTTP response的類型之后够委,我們就可以很方便的處理Alamofire返回的序列化結(jié)果了。
處理序列化之后的String和JSON
首先來看String:
Alamofire.request(.GET, "https://httpbin.org/ip")
.response { request, response, data, error in
print(request)
print(response)
print(data)
print(data.dynamicType)
print(error)
print(error.dynamicType)
}
.responseString { response in
print("String:====================")
switch response.result {
case .Success(let str):
print("\(str.dynamicType)")
case .Failure(let error):
print("\(error)")
}
}
在這里怖现,Alamofire的各種response方法是可以串聯(lián)在一起的茁帽,由于response.result是一個enum,我們可以通過一個switch...case來處理它的兩種結(jié)果屈嗤,并且在.Success里潘拨,直接讀取它的associated value作為返回的字符串;在.Failure里讀取包含錯誤信息的對象恢共。
接下來战秋,我們來看JSON。為了得到一個相對復(fù)雜一些的JSON讨韭,這次,我們請求httpbin的/get癣蟋,然后透硝,會得到一個類似下面的返回結(jié)果:
{
"args": {},
"headers": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate, sdch",
"Accept-Language": "en-US,en;q=0.8,zh-CN;q=0.6,zh;q=0.4",
"Cookie": "_ga=GA1.2.1123360294.1453608486; _gat=1",
"Host": "httpbin.org",
"Referer": "https://httpbin.org/",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2646.0 Safari/537.36"
},
"origin": "106.185.45.242",
"url": "https://httpbin.org/get"
}
首先我們把之前例子里的請求的URL修改成https://httpbin.org/get:
Alamofire.request(.GET, "https://httpbin.org/get")
然后,把responseJSON串聯(lián)在responseString后面:
Alamofire.request(.GET, "https://httpbin.org/get")
.responseString(completionHandler: { response in
print("String ===============")
switch response.result {
case .Success(let str):
print("\(str.dynamicType)")
print("\(str)")
case .Failure(let error):
print("\(error)")
}
})
.responseJSON(completionHandler: { response in
print("JSON ================")
})
為了更好的理解解析JSON的方式疯搅,我們來看個圖:
對于一個JSON的返回結(jié)果來說濒生,它的所有KEY的類型,都是String幔欧,而VALUE的類型罪治,有可能是一個String值,有可能是一個數(shù)組礁蔗,還有可能是一個對象觉义。因此,Alamofire只能用一個AnyObject類型的對象表示它浴井。通常晒骇,我們要把它轉(zhuǎn)化成一個<key style="box-sizing: border-box;">NSDictionary</key>或Dictionary<String, AnyObject>類型的對象。然后偏化,根據(jù)訪問的KEY的類型励翼,做進一步的類型轉(zhuǎn)換:
- 讀取key1時闲先,把結(jié)果轉(zhuǎn)化成String
- 讀取key2時,把結(jié)果轉(zhuǎn)化成Array
- 讀取key3時瘤缩,把結(jié)果轉(zhuǎn)化成其它的Dictionary
理解了處理JSON的方法,我們就可以在responseJSON中處理httpbin的返回結(jié)果了:
Alamofire.request(.GET, "https://httpbin.org/get")
.responseJSON(completionHandler: { response in
print("JSON ================")
switch response.result {
case .Success(let json):
let dict = json as! Dictionary
let origin = dict["origin"] as! String
let headers = dict["headers"] as! Dictionary
print("origin: \(origin)")
let ua = headers["User-Agent"]
print("UA: \(ua)")
case .Failure(let error):
print("\(error)")
}
})
在上面的代碼中我們可以看到伦泥,在讀取"origin"和"headers"時剥啤,我們并不用類型轉(zhuǎn)換何暮,并且,在下面的結(jié)果里铐殃,變量origin和ua各自代表了JSON中的內(nèi)容: