228400
AFNetworkingCOCAlamofire網(wǎng)絡(luò)庫
本章節(jié)由CocoaChina翻譯組成員星夜暮晨(博客)翻譯自raywenderlich:Beginning Alamofire Tutorial硝岗,敬請(qǐng)勘誤。
AFNetworking是 iOS 和 OS X 上最受歡迎的第三方庫之一。它曾在我們的2012年的讀者評(píng)選中榮獲2012年度最佳 iOS 庫稱號(hào)闷哆。它同樣也在Github上面獲得了14000多個(gè) stars 和4000多個(gè) forks,是使用最廣的開源項(xiàng)目之一靖避。
最近哩俭,AFNetworking 的作者Mattt Thompson提交了一個(gè)新的類似于 AFNetworking 的網(wǎng)絡(luò) 基礎(chǔ)庫,并且是專門使用最新的 Swift 語言來編寫的椅野,名為:Alamofire
AFNetwork 的前綴 AF 是 Alamofire 的縮寫,因此這個(gè)新的庫名稱其實(shí)是根據(jù) Swift 的約定來進(jìn)行命名的籍胯。
在本教程的第一部分中竟闪,我們將帶領(lǐng)大家使用 Alamofire 來制作一個(gè)圖片庫應(yīng)用,其來源是500px.com杖狼。在這個(gè)過程中炼蛤,您可以學(xué)習(xí)到如何使用 Alamofire 中的重要組件,以及了解在應(yīng)用中處理網(wǎng)絡(luò)請(qǐng)求的某些重要的知識(shí)點(diǎn)蝶涩。
本教程的第二部分將基于第一部分所制作的應(yīng)用理朋,并為其增加一些好用的功能。您可以通過這個(gè)過程學(xué)習(xí)到更多高級(jí)的 Alamofire 用法绿聘。
本教程假定您已熟悉 Swift 語言以及 iOS 開發(fā)嗽上。如果不是的話,請(qǐng)參閱我們的其他教程熄攘。還有炸裆,本教程使用 Xcode 6.1 作為開發(fā)環(huán)境(而不是 Xcode 6.2 beta)。
>?提示:如果您已經(jīng)熟悉了 Alamofire 的基本用法鲜屏,那么您可以直接跳到本文的第二部分烹看。但是請(qǐng)確保您已擁有消費(fèi)者密鑰(cunsumer key),然后參照下文在應(yīng)用中替換它洛史。
讓我們開始吧
首先下載本次教程的初始項(xiàng)目惯殊。這個(gè)項(xiàng)目中提供了在本教程中需要的全部 UI。這有助于您能夠?qū)⒆⒁饬械綄W(xué)習(xí) Alamofire 的使用上來也殖,而不是花費(fèi)大量時(shí)間來研究 UI土思。
在 Xcode 中打開這個(gè)項(xiàng)目务热,并跳轉(zhuǎn)到Main.storyboard來:
我們應(yīng)用的主屏幕使用UITabBarController這個(gè)常用的 UI 樣式。我們的標(biāo)簽控制器中包含有兩個(gè)標(biāo)簽己儒,每個(gè)標(biāo)簽都有它們自己的UINavigationController頁面崎岂。第一個(gè)標(biāo)簽讓用戶瀏覽熱門圖片。第二個(gè)標(biāo)簽讓用戶瀏覽他們已保存的文件闪湾。兩個(gè)標(biāo)簽都使用`UICollectionViewController`來向用戶展示圖片冲甘。故事板中同樣也包含了兩個(gè)獨(dú)立的視圖控制器,在接下來的教程中我們將會(huì)用到它們途样。
生成并運(yùn)行該應(yīng)用江醇,您首先會(huì)看到一個(gè)不停在加載的加載控件:
這看起來一點(diǎn)也不高端大氣上檔次……可以說沒什么可看的。但是很快我們將會(huì)借助 Alamofire 來讓他逼格高起來何暇。
> 提示:如果您很熟悉 AFNetworking 的使用陶夜,那么您可能會(huì)覺得下一節(jié)我們將談?wù)?CocoaPods。但是就目前來說裆站,還沒有一個(gè)通過 CocoaPods 來整合 Swift 庫的簡單方法条辟。
雖然說可能有人發(fā)現(xiàn)了該問題的解決方法,并將其上傳到網(wǎng)上宏胯,但是下面的步驟仍然還是使用手動(dòng)復(fù)制代碼到項(xiàng)目中的這樣一個(gè)可靠的方法羽嫡。
要獲取最新版本的 Alamofire,前往https://github.com/Alamofire/Alamofire然后單擊網(wǎng)頁右邊的Download ZIP按鈕胳嘲。接著在 Finder 中打開起始項(xiàng)目文件夾厂僧,然后將Alamofire-master文件夾拖入到您的主項(xiàng)目文件夾中扣草。
打開Alamofire-master文件夾(現(xiàn)在它位于您的項(xiàng)目文件夾中)了牛,然后將Alamofire.xcodeprij文件(注意是藍(lán)色圖標(biāo)!不是白色圖標(biāo)3矫睢)直接拖進(jìn) Xcode 中的 Photomania 項(xiàng)目下面鹰祸,如下圖所示:
接下來,單擊Photomania項(xiàng)目密浑,進(jìn)入General窗口蛙婴。滾動(dòng)到Embedded Binaries項(xiàng),然后單擊其下方的 + 號(hào)尔破。選擇Alamofire.framework街图,最后點(diǎn)擊Add完成添加。
生成并運(yùn)行您的項(xiàng)目以確保沒有任何錯(cuò)誤出現(xiàn)懒构,然后就可以進(jìn)入到下一節(jié)內(nèi)容了餐济。
使用 Alamofire 來檢索數(shù)據(jù)
您可能會(huì)覺得,為什么我們要使用 Alamofire 呢胆剧?明明蘋果已經(jīng)提供了NSURLSession類以及其相關(guān)類絮姆,以便讓我們通過 HTTP 來下載相應(yīng)內(nèi)容。為什么我們還要使用第三方庫來讓這件事情變得復(fù)雜呢?
簡單來說篙悯,Alamofire 其實(shí)是基于`NSURLSession`的蚁阳,但是它可以免去您寫樣板(boilerplate)代碼的麻煩,并且可以讓網(wǎng)絡(luò)模塊的代碼更為簡單易用鸽照。您可以通過一些非常簡單的操作來訪問 Internet 上的數(shù)據(jù)螺捐,并且寫出來的代碼也會(huì)更加清晰明了、簡單易讀移宅。
要使用 Alamofire 的話归粉,首先需要導(dǎo)入它。請(qǐng)打開PhotoBrowserCollectionViewController.swift文件漏峰,然后在文件頂部添加如下代碼:
1
import?Alamofire
您需要在每個(gè)使用了 Alamofire 類以及函數(shù)的文件中添加這條`import`語句糠悼。
接下來,在`setupView()`下方的`viewDidLoad()`方法中添加如下代碼:
1
2
3
4Alamofire.request(.GET,"https://api.500px.com/v1/photos").responseJSON()?{
(_,?_,?data,?_)in
println(data)
}
過會(huì)兒我會(huì)對(duì)其做出詳細(xì)解釋浅乔,但是首先您需要生成并運(yùn)行該應(yīng)用倔喂,這個(gè)時(shí)候您會(huì)在控制臺(tái)中看到如下信息:
1
2
3
4Optional({
error?="Consumer?key?missing.";
status?=?401;
})
您可能不明白它說了什么鬼,不過您已經(jīng)成功地使用 Alamofire 來實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求了靖苇!您向 Internet 上的資源發(fā)出了一個(gè)請(qǐng)求席噩,然后返回了一個(gè)JSON 數(shù)據(jù)。
下面來解釋一下那些代碼到底做了些什么:
·Alamofire.request(_:_)接收兩個(gè)參數(shù):method和URLString贤壁。其中悼枢,method 通常是.GET、.POST脾拆;URLString通常是您想要訪問的內(nèi)容的 URL馒索。其將返回一個(gè)Alamofire.Request對(duì)象。
·通常情況下名船,您只需將請(qǐng)求對(duì)象鏈接到響應(yīng)方法上绰上。例如,在上面的代碼中渠驼,請(qǐng)求對(duì)象簡單地調(diào)用了responseJSON()方法蜈块。當(dāng)網(wǎng)絡(luò)請(qǐng)求完畢后,responseJSON()方法會(huì)調(diào)用我們所提供的閉包迷扇。在我們的示例中百揭,我們只是簡單的將經(jīng)過解析的 JSON 輸出到控制臺(tái)中。
·調(diào)用responseJSON方法意味著您期望獲得一個(gè) JSON 數(shù)據(jù)蜓席。在我們的示例中器一,Alamofire 試圖解析響應(yīng)數(shù)據(jù)并返回一個(gè) JSON 對(duì)象∥痛玻或者盹舞,您可以使用responsePropertyList來請(qǐng)求獲得一個(gè)屬性列表产镐,也可以使用responseString來請(qǐng)求獲得一個(gè)初始字符串。在本教程后面踢步,您將了解更多關(guān)于響應(yīng)序列化方法的使用方式癣亚。
您可以從控制臺(tái)中看到輸出的響應(yīng)數(shù)據(jù),服務(wù)器報(bào)告您需要一個(gè)名為consumer key的東西获印。在我們繼續(xù)使用 Alamofire 之前述雾,我們需要從 500px 網(wǎng)站的 API 中獲取一個(gè)密鑰。
獲取消費(fèi)者密鑰
前往https://500px.com/signup兼丰,然后使用您的郵箱免費(fèi)注冊(cè)玻孟,或者使用您的 Facebook 、Twitter 或者 Google 帳號(hào)登錄鳍征。
一旦您完成了注冊(cè)流程黍翎,那么前往https://500px.com/settings/applications并單擊"Register your application"。
您會(huì)看到如下所示的對(duì)話框:
紅色大箭頭指向的那些文本框里面的內(nèi)容都是必填的艳丛。使用Alamofire Tutorial作為Application Name匣掸,然后使用iOS App作為Description。目前您的應(yīng)用還沒有Application URL氮双,但是您可以隨意輸一個(gè)有效的網(wǎng)址來完成應(yīng)用注冊(cè)碰酝,您可以使用raywenderlich.com^_^。
最后戴差,在Developer’s Email中輸入您的郵箱地址送爸,然后單擊復(fù)選框來接受使用協(xié)議。
接著暖释,單擊Register按鈕袭厂,您會(huì)看到一個(gè)如下所示的框:
單擊See application details鏈接,然后它會(huì)彈出詳細(xì)信息饭入,這時(shí)候您就可以定義您的消費(fèi)者密鑰了嵌器,如下所示:
從該頁面中復(fù)制出您的消費(fèi)者密鑰肛真,然后返回 Xcode谐丢,然后用如下代碼替換掉目前為止您唯一添加的代碼:
1
2
3
4Alamofire.request(.GET,"https://api.500px.com/v1/photos",?parameters:?["consumer_key":"PASTE_YOUR_CONSUMER_KEY_HERE"]).responseJSON()?{
(_,?_,?JSON,?_)in
println(JSON)
}
請(qǐng)確保您已經(jīng)用復(fù)制的消費(fèi)者密鑰來替換掉PASTE_YOUR_CONSUMER_KEY_HERE。
生成并運(yùn)行您的應(yīng)用蚓让,這時(shí)您會(huì)在控制臺(tái)中看見海量的輸出:
上述所有的輸出意味著您成功地下載到了含有一些照片信息的 JSON乾忱。
JSON 數(shù)據(jù)中包含了一些圖片集的屬性、一些頁面信息历极,以及一個(gè)圖片數(shù)組窄瘟。這里是我得到的搜索結(jié)果(您的可能會(huì)略有不同):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29{
"feature":"popular",
"filters":?{
"category":false,
"exclude":false
},
"current_page":?1,
"total_pages":?250,
"total_items":?5000,
"photos":?[
{
"id":?4910421,
"name":"Orange?or?lemon",
"description":"",
.
.
.
}
},
{
"id":?4905955,
"name":"R?E?S?I?G?N?E?D",
"description":"From?the?past?of?Tagus?River,?we?have?History?and?memories,?some?of?them?abandoned?and?disclaimed?in?their?margins?...",
.
.
.
}
]
}
現(xiàn)在我們已經(jīng)擁有了 JSON 數(shù)據(jù),接下來我們就可以好好地利用它了趟卸。
使用如下代碼替換掉viewDidLoad()中的 println(JSON)方法:
1
2
3
4
5
6
7
8
9let?photoInfos?=?(JSON!.valueForKey("photos")?as?[NSDictionary]).filter({
($0["nsfw"]?as?Bool)?==false
}).map?{
PhotoInfo(id:?$0["id"]?as?Int,?url:?$0["image_url"]?as?String)
}
self.photos.addObjectsFromArray(photoInfos)
self.collectionView.reloadData()
上述代碼將 JSON 數(shù)據(jù)轉(zhuǎn)變?yōu)榱烁子诠芾淼腵PhotoInfo`數(shù)組對(duì)象蹄葱。這些對(duì)象只是簡化掉了圖片 ID 和 URL 屬性的存儲(chǔ)桶(bucket)氏义。您同樣可以發(fā)現(xiàn)代碼過濾掉了一些……呃……您不希望出現(xiàn)的一些圖片。
上述代碼同樣也重新加載了集合視圖图云。初始項(xiàng)目的示例代碼基于我們剛剛填充的`photos`惯悠,來創(chuàng)建集合視圖的單元。
生成并運(yùn)行您的應(yīng)用竣况,這時(shí)加載控件加載一會(huì)兒便消失克婶。如果您仔細(xì)觀察的話,您會(huì)發(fā)現(xiàn)一堆灰黑色方形單元:
離我們的目標(biāo)越來越接近了丹泉,加油情萤!
我們?nèi)匀欢ㄎ坏?b>PhotoBrowserCollectionViewController.swift,在`collectionView(_: cellForItemAtIndexPath:)`方法中的return cell前加上如下的代碼:
1
2
3
4
5
6
7
8let?imageURL?=?(photos.objectAtIndex(indexPath.row)?as?PhotoInfo).url
Alamofire.request(.GET,?imageURL).response()?{
(_,?_,?data,?_)in
let?image?=?UIImage(data:?data!?as?NSData)
cell.imageView.image?=?image
}
上述的代碼為`photos`數(shù)組中的圖片創(chuàng)建了另外的 Alamofire 請(qǐng)求摹恨。由于這是一個(gè)圖片請(qǐng)求筋岛,因此我們使用的是一個(gè)簡單的request方法,其在NSData的blob 中返回響應(yīng)晒哄。接下來我們直接把數(shù)據(jù)放入到一個(gè)UIImage的實(shí)例中泉蝌,然后反過來將實(shí)例放入早已存在于示例項(xiàng)目中的imageView 當(dāng)中。
再一次生成并運(yùn)行您的應(yīng)用揩晴,這時(shí)應(yīng)當(dāng)出現(xiàn)一個(gè)圖片集合勋陪,與下圖相似:
對(duì)于 Alamofire 的工作效果想必您現(xiàn)在已經(jīng)心中有數(shù),但是您不會(huì)想在每次從服務(wù)器請(qǐng)求數(shù)據(jù)的時(shí)候硫兰,要不停的復(fù)制诅愚、粘貼 API 地址,以及添加消費(fèi)者密鑰劫映。除了這一點(diǎn)非常讓人不爽外违孝,如果 API 地址發(fā)生了改變,那么您可能不得不再次創(chuàng)建一個(gè)新的消費(fèi)者密鑰泳赋。
幸運(yùn)的是雌桑,Alamofire對(duì)于這個(gè)問題有著良好的解決方案。
創(chuàng)建請(qǐng)求路由
打開Five100px.swift,然后找到struct Five100px祖今,其中定義了enum ImageSize校坑。這是一個(gè)簡單的基于 500px.com 的 API 文件的結(jié)構(gòu)體。
在使用 Alamofire 之前千诬,您需要在文件頂部添加下述的必要聲明:
1
import?Alamofire
現(xiàn)在耍目,在struct Five100px中的enum ImageSize上方添加下述代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30enum?Router:?URLRequestConvertible?{
static?let?baseURLString?="https://api.500px.com/v1"
static?let?consumerKey?="PASTE_YOUR_CONSUMER_KEY_HERE"
casePopularPhotos(Int)
casePhotoInfo(Int,?ImageSize)
caseComments(Int,?Int)
varURLRequest:?NSURLRequest?{
let?(path:?String,?parameters:?[String:?AnyObject])?=?{
switchself?{
case.PopularPhotos?(let?page):
let?params?=?["consumer_key":?Router.consumerKey,"page":"\(page)","feature":"popular","rpp":"50","include_store":"store_download","include_states":"votes"]
return("/photos",?params)
case.PhotoInfo(let?photoID,?let?imageSize):
varparams?=?["consumer_key":?Router.consumerKey,"image_size":"\(imageSize.rawValue)"]
return("/photos/\(photoID)",?params)
case.Comments(let?photoID,?let?commentsPage):
varparams?=?["consumer_key":?Router.consumerKey,"comments":"1","comments_page":"\(commentsPage)"]
return("/photos/\(photoID)/comments",?params)
}
}()
let?URL?=?NSURL(string:?Router.baseURLString)
let?URLRequest?=?NSURLRequest(URL:?URL!.URLByAppendingPathComponent(path))
let?encoding?=?Alamofire.ParameterEncoding.URL
returnencoding.encode(URLRequest,?parameters:?parameters).0
}
}
這就是我們所創(chuàng)建的路由,它為我們的 API 調(diào)用方法創(chuàng)建合適的URLString實(shí)例徐绑。它是一個(gè)簡單的遵守URLRequestConertible協(xié)議的enum類型邪驮,這個(gè)協(xié)議是在 Alamofire 當(dāng)中定義的。當(dāng)有枚舉類型采用該協(xié)議的時(shí)候傲茄,該類型就必須含有一個(gè)名為URLRequest的NSURLRequest類型變量毅访。
這個(gè)路由含有兩個(gè)靜態(tài)常量:API 的baseURLString以及consumerKey沮榜。(最后一次聲明,請(qǐng)`PASTE_YOUR_CONSUMER_KEY_HERE`替換為您自己的消費(fèi)者密鑰)現(xiàn)在喻粹,這個(gè)路由可以在必要的時(shí)候向最終的`URLString`中添加消費(fèi)者密鑰敞映。
您的應(yīng)用擁有三個(gè) API 終結(jié)點(diǎn)(endpoint):一個(gè)用來取出熱門照片列表,一個(gè)用來取出某個(gè)特定照片的具體信息磷斧,一個(gè)用來取出某個(gè)照片的評(píng)論振愿。路由將會(huì)借助三個(gè)相應(yīng)的`case`聲明來處理這三個(gè)終結(jié)點(diǎn),每個(gè)終結(jié)點(diǎn)都會(huì)接收一到兩個(gè)參數(shù)弛饭。
我們已經(jīng)定義了`var URLRequest: NSURLRequest`作為計(jì)算(computed)屬性冕末。這意味著每次我們使用`enum`的時(shí)候,它都會(huì)構(gòu)造出基于特定`case`和其參數(shù)的最終 URL侣颂。
這里有一個(gè)示例代碼片段档桃,說明了上述的邏輯關(guān)系:
1
2
3
4Five100px.Router.PhotoInfo(10000,?Five100px.ImageSize.Large)
//?URL:https://api.500px.com/v1/photos/10000?consumer_key=xxxxxx&image_size=4
//https://api.500px.com/v1+??/photos/10000??+???consumer_key=xxxxxx&image_size=4
//?=?baseURLString??+??path??+??encoded?parameters
在上面的示例中,代碼路由通過照片信息 API 的終結(jié)點(diǎn)來尋找一個(gè) ID 為10000的大尺寸圖片憔晒。注釋行將 URL 的結(jié)構(gòu)進(jìn)行了拆分藻肄。在這個(gè)示例中,URL 由三個(gè)部分組成:`baseURLString`拒担、`path`(?前面的那一部分)以及`[String: AnyObject]`字典嘹屯,其中包含有傳遞給 API 終結(jié)點(diǎn)的參數(shù)。
對(duì)于`path`來說从撼,返回元組的第一個(gè)元素可以用以下的字符串形式返回:
1
"/photos/\(photoID)"http://?"/photos/10000"
和響應(yīng)解析類似州弟,請(qǐng)求參數(shù)可以被編碼為 JSON、屬性列表或者是字符串低零。通常情況下使用簡單的字符串參數(shù)婆翔,和上面我們所做的類似。
如果您打算在您自己的項(xiàng)目中使用路由掏婶,您必須對(duì)它的運(yùn)行機(jī)制相當(dāng)熟悉啃奴。為此,請(qǐng)嘗試搞清楚要如何構(gòu)造出以下的 URL:
https://api.foursquare.com/v2/users/{USER_ID}/lists?v=20131016&group=created
您是怎么做的呢雄妥?如果您不是百分百確定答案最蕾,請(qǐng)花一點(diǎn)時(shí)間來分析下面的代碼,直到您完全搞明白其工作原理:
> 解決方案:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15static?let?baseURLString?="https://api.foursquare.com/v2"
caseUserLists(Int)
varURLRequest:?NSURLRequest?{
let?(path:?String,?parameters:?[String:?AnyObject])?=?{
switchself?{
case.?UserLists?(let?userID):
let?params?=?["v":"20131016","group":"created"]
return("/users/\(userID)/lists",?params)
}
}()
.
.
.
這里您需要為枚舉添加其他的 case茎芭,比如說用戶列表揖膜,它們都設(shè)置有合適的參數(shù)和路徑誓沸。
加載更多圖片
好的梅桩,現(xiàn)在應(yīng)用目前顯示的照片只有一個(gè)頁面,但是我們想要瀏覽更多照片以找到我們心儀的內(nèi)容拜隧。多多益善對(duì)吧宿百?
打開PhotoBrowserCollectionViewController.swift趁仙,然后在 let refreshControl = UIRefreshControl()語句下方添加如下代碼:
1
2varpopulatingPhotos?=false
varcurrentPage?=?1
這里我們定義了兩個(gè)變量,來記錄當(dāng)前是否在更新照片垦页,以及當(dāng)前我們正在瀏覽的是哪一個(gè)照片頁面雀费。
接下來,用以下代碼替換當(dāng)前`viewDidLoad()`的聲明:
1
2
3
4
5
6
7override?func?viewDidLoad()?{
super.viewDidLoad()
setupView()
populatePhotos()
}
這里我們用populatePhotos()函數(shù)來替換了先前的 Alamofire 請(qǐng)求痊焊。之后我們就要實(shí)現(xiàn)populatePhotos()函數(shù)的聲明盏袄。
同樣的,在`handleRefresh()`上方添加兩個(gè)函數(shù)薄啥,如下所述:
啊……好長一段代碼辕羽,對(duì)吧?下面是對(duì)每個(gè)注釋部分的詳細(xì)解釋:
1. 一旦您滾動(dòng)超過了 80% 的頁面垄惧,那么scrollViewDidScroll()方法將會(huì)加載更多的圖片刁愿。
2.populatePhotos()方法在currentPage當(dāng)中加載圖片,并且使用populatingPhotos作為標(biāo)記到逊,以防止還在加載當(dāng)前界面時(shí)加載下一個(gè)頁面铣口。
3. 這里我們首次使用了我們創(chuàng)建的路由。只需將頁數(shù)傳遞進(jìn)去觉壶,它將為該頁面構(gòu)造 URL 字符串脑题。500px.com 網(wǎng)站在每次 API 調(diào)用后返回大約50張圖片,因此您需要為下一批照片的顯示再次調(diào)用路由铜靶。
4. 要注意旭蠕,.responseJSON()后面的代碼塊:completion handler(完成處理方法)必須在主線程運(yùn)行。如果您正在執(zhí)行其他的長期運(yùn)行操作旷坦,比如說調(diào)用 API掏熬,那么您必須使用 GCD 來將您的代碼調(diào)度到另一個(gè)隊(duì)列運(yùn)行。在本示例中秒梅,我們使用`DISPATCH_QUEUE_PRIORITY_HIGH`來運(yùn)行這個(gè)操作旗芬。
5. 您可能會(huì)關(guān)心 JSON 數(shù)據(jù)中的photos關(guān)鍵字,其位于數(shù)組中的字典中捆蜀。每個(gè)字典都包含有一張圖片的信息疮丛。
6. 我們使用 Swift 的filter函數(shù)來過濾掉 NSFW 圖片(Not Safe For Work)
7. map函數(shù)接收了一個(gè)閉包,然后返回一個(gè)PhotoInfo對(duì)象的數(shù)組辆它。這個(gè)類是在Five100px.swift當(dāng)中定義的誊薄。如果您查看這個(gè)類的源碼,那么就可以看到它重寫了isEqual和hash這兩個(gè)方法锰茉。這兩個(gè)方法都是用一個(gè)整型的id屬性呢蔫,因此排序和唯一化(uniquing)PhotoInfo對(duì)象仍會(huì)是一個(gè)比較快的操作
8. 接下來我們會(huì)在添加新的數(shù)據(jù)前存儲(chǔ)圖片的當(dāng)前數(shù)量,使用它來更新collectionView.
9. 如果有人在我們滾動(dòng)前向 500px.com 網(wǎng)站上傳了新的圖片飒筑,那么您所獲得的新的一批照片將可能會(huì)包含一部分已下載的圖片片吊。這就是為什么我們定義var photos = NSMutableOrderedSet()為一個(gè)組绽昏。由于組內(nèi)的項(xiàng)目必須唯一,因此重復(fù)的圖片不會(huì)再次出現(xiàn)
10. 這里我們創(chuàng)建了一個(gè)NSIndexPath對(duì)象的數(shù)組俏脊,并將其插入到collectionView.
11. 在集合視圖中插入項(xiàng)目全谤,請(qǐng)?jiān)谥麝?duì)列中完成該操作,因?yàn)樗械?UIKit 操作都必須運(yùn)行在主隊(duì)列中
生成并運(yùn)行您的應(yīng)用爷贫,然后緩慢向下滑動(dòng)圖片认然。您可以看到新的圖片將持續(xù)加載:
不斷加快滑動(dòng)的速度,注意到問題沒有漫萄?對(duì)的季眷,滾動(dòng)操作不是很穩(wěn)定,有些許遲鈍的感覺卷胯。這并不是我們想要提供給用戶的體驗(yàn)子刮,但是我們?cè)谙乱还?jié)中就可以修正這個(gè)問題了。
創(chuàng)建自定義響應(yīng)序列化方法(Serializer)
您已經(jīng)看到窑睁,我們?cè)?Alamofire 中使用所提供的 JSON挺峡、字符串,以及屬性列表序列化方法是一件非常簡單的事情担钮。但是有些時(shí)候橱赠,您可能會(huì)想要?jiǎng)?chuàng)建自己的自定義相應(yīng)序列化。例如箫津,您可以寫一個(gè)響應(yīng)序列化方法來直接接收UIIMage狭姨,而不是將UIImage轉(zhuǎn)化為NSData來接收。
在本節(jié)中苏遥,您將學(xué)習(xí)如何創(chuàng)建自定義響應(yīng)序列化方法饼拍。
打開Five100px.swift,然后在靠近文件頂部的地方田炭,也就是import Alamofire語句下面添加如下代碼:
要?jiǎng)?chuàng)建一個(gè)新的響應(yīng)序列化方法师抄,我們首先應(yīng)當(dāng)需要一個(gè)類方法,其返回Serializer閉包(比如說上面所寫的imageResponseSerializer())教硫。這個(gè)閉包在 Alamofire 中被類型命名叨吮,其接收三個(gè)參數(shù)并返回所示的兩個(gè)參數(shù),如下所示:
1
public?typealias?Serializer?=?(NSURLRequest,?NSHTTPURLResponse?,?NSData?)?->?(AnyObject?,?NSError?)
類方法(例如imageResponseSerializer())接收底層的NSURLSession請(qǐng)求以及和響應(yīng)對(duì)象一起的基本NSData數(shù)據(jù)實(shí)現(xiàn)方法(從服務(wù)器傳來的)瞬矩,來作為參數(shù)茶鉴。該方法接下來使用這些對(duì)象來序列化,并將其輸入到一個(gè)有意義的數(shù)據(jù)結(jié)構(gòu)中景用,然后將其從方法中返回涵叮,它同樣也會(huì)返回在這個(gè)過程中發(fā)生的錯(cuò)誤。在我們的示例中,我們使用UIImage來將數(shù)據(jù)轉(zhuǎn)化為圖片對(duì)象围肥。
通常情況下剿干,當(dāng)您創(chuàng)建了一個(gè)響應(yīng)序列化方法后蜂怎,您可能還會(huì)向創(chuàng)建一個(gè)新的響應(yīng)處理方法來對(duì)其進(jìn)行處理穆刻,并讓其更加易用。我們使用.responseImage()方法來完成這項(xiàng)任務(wù)杠步。這個(gè)方法的操作很簡單:它使用completionHandler氢伟,一個(gè)以閉包形式的代碼塊。一旦我們從服務(wù)器中序列化了數(shù)據(jù)幽歼,那么這個(gè)代碼塊將會(huì)運(yùn)行朵锣。我們所需要做的就是在響應(yīng)處理方法中調(diào)用 Alamofire 自己的通用.response()響應(yīng)處理方法。
讓我們開始讓它工作起來甸私。打開PhotoBrowserCollectionViewController.swift诚些,然后在PhotoBrowserCollectionViewCell中的imageView屬性下面,添加如下一個(gè)屬性:
1
varrequest:?Alamofire.Request?
這個(gè)屬性會(huì)為這個(gè)單元存儲(chǔ) Alamofire 的請(qǐng)求來加載圖片
現(xiàn)在將collectionView(_: cellForItemAtIndexPath:)的內(nèi)容替換為下面所示的代碼:
生成并運(yùn)行您的應(yīng)用皇型,再次滾動(dòng)圖片诬烹,您會(huì)發(fā)現(xiàn)滾動(dòng)變得流暢了。
為什么會(huì)流暢呢弃鸦?
那么我們到底做了些什么來讓滾動(dòng)變得流暢了呢绞吁?其關(guān)鍵就是collectionView(_: cellForItemAtIndexPath:)中的代碼。但是在我們解釋這段代碼之前唬格,我們需要向您解釋網(wǎng)絡(luò)調(diào)用的異步性家破。
Alamofire 的網(wǎng)絡(luò)調(diào)用是異步請(qǐng)求方式。這意味著提交網(wǎng)絡(luò)請(qǐng)求不會(huì)阻止剩余代碼的執(zhí)行购岗。網(wǎng)絡(luò)請(qǐng)求可能會(huì)執(zhí)行很長時(shí)間才能得到返回結(jié)果汰聋,但是您不會(huì)希望在等待圖片下載的時(shí)候 UI 被凍結(jié)。
也就是說喊积,實(shí)現(xiàn)異步請(qǐng)求是一個(gè)極大的挑戰(zhàn)马僻。如果在發(fā)出請(qǐng)求之后到從服務(wù)器接收到響應(yīng)的這段時(shí)間中,UI 發(fā)生了改變的話怎么辦注服?
例如韭邓,UICollectionView擁有內(nèi)部的單元出列機(jī)制。創(chuàng)建新的單元對(duì)系統(tǒng)來說開銷很大溶弟,因此集合視圖將重用不在屏幕上顯示的現(xiàn)有單元女淑,而不是不停創(chuàng)建新的單元。
這意味著同一個(gè)單元對(duì)象辜御,會(huì)被不停地重復(fù)使用鸭你。因此在發(fā)出 Alamofire 請(qǐng)求之后到接收到圖片信息響應(yīng)之前,用戶將單元滾動(dòng)出屏幕并且刪除圖片的操作將成為可能。單元可能會(huì)出列袱巨,并且準(zhǔn)備顯示另一個(gè)圖片阁谆。
在上述的代碼中,我們完成了兩件事來解決這個(gè)問題愉老。第一件事是场绿,當(dāng)一個(gè)單元出列后,我們通過設(shè)值為nil的方法來清除圖片嫉入。這個(gè)操作確保我們不會(huì)顯示原先的圖片焰盗;第二件事是,我們的請(qǐng)求完成處理方法將檢查單元的 URL 是否和請(qǐng)求的 URL 相等咒林。如果不相等的話熬拒,顯然單元已經(jīng)擁有了另外的圖片,那么完成處理方法將不會(huì)浪費(fèi)其生命周期來為單元設(shè)置錯(cuò)誤的圖片垫竞。
接下來該何去何從澎粟?
您可以在這里下載本教程第一部分的最終版本項(xiàng)目。
> 提示:如果您打算直接使用上面的的最終版本欢瞪,那么千萬不要忘記在前面的教程中所說的活烙,用您的消費(fèi)者密鑰酌情替換Five100px.swift中的響應(yīng)內(nèi)容。
現(xiàn)在引有,多虧了 Alamofire瓣颅,您的應(yīng)用擁有了基本的照片瀏覽功能。現(xiàn)在您已經(jīng)學(xué)會(huì)了如何使用 Alamofire 發(fā)送 GET 請(qǐng)求譬正、傳遞參數(shù)宫补、創(chuàng)建請(qǐng)求路由,甚至學(xué)會(huì)了創(chuàng)建您自己的響應(yīng)序列化方法曾我。