(轉(zhuǎn)載)使用 Alamofire 輕松實(shí)現(xiàn) Swift 網(wǎng)絡(luò)請(qǐng)求

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)序列化方法曾我。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末粉怕,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子抒巢,更是在濱河造成了極大的恐慌贫贝,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蛉谜,死亡現(xiàn)場離奇詭異稚晚,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)型诚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門客燕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人狰贯,你說我怎么就攤上這事也搓∩屠” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵傍妒,是天一觀的道長幔摸。 經(jīng)常有香客問我,道長颤练,這世上最難降的妖魔是什么既忆? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮昔案,結(jié)果婚禮上尿贫,老公的妹妹穿的比我還像新娘电媳。我一直安慰自己踏揣,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布匾乓。 她就那樣靜靜地躺著捞稿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪拼缝。 梳的紋絲不亂的頭發(fā)上娱局,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音咧七,去河邊找鬼衰齐。 笑死,一個(gè)胖子當(dāng)著我的面吹牛继阻,可吹牛的內(nèi)容都是我干的耻涛。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼瘟檩,長吁一口氣:“原來是場噩夢啊……” “哼抹缕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起墨辛,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤卓研,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后睹簇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體奏赘,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年太惠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了磨淌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡垛叨,死狀恐怖伦糯,靈堂內(nèi)的尸體忽然破棺而出柜某,到底是詐尸還是另有隱情,我是刑警寧澤敛纲,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布喂击,位于F島的核電站,受9級(jí)特大地震影響淤翔,放射性物質(zhì)發(fā)生泄漏翰绊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一旁壮、第九天 我趴在偏房一處隱蔽的房頂上張望监嗜。 院中可真熱鬧,春花似錦抡谐、人聲如沸裁奇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽刽肠。三九已至,卻和暖如春免胃,著一層夾襖步出監(jiān)牢的瞬間音五,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國打工羔沙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留躺涝,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓扼雏,卻偏偏與公主長得像坚嗜,于是被迫代替她去往敵國和親棉磨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子螃宙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)壹瘟,斷路器其障,智...
    卡卡羅2017閱讀 134,651評(píng)論 18 139
  • Alamofire 是一款 Swift 寫的 HTTP 網(wǎng)絡(luò)請(qǐng)求庫 前言 本篇內(nèi)容為 Alamofire 官方 R...
    zongmumask閱讀 20,773評(píng)論 6 66
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,071評(píng)論 25 707
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫银室、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,093評(píng)論 4 62
  • 個(gè)人簡歷 每個(gè)人都有一份自己的簡歷励翼,作為設(shè)計(jì)師肯定是要有最好的一面展現(xiàn) 1.黑色的布局蜈敢,從上到下名字的圓形有一種感...
    1997xyh閱讀 194評(píng)論 0 0