Alamofire網絡庫基礎教程

原文 Beginning Alamofire Tutorialhtml

原文做者 Essan Parto
譯者 星夜暮晨(QQ:412027805)ios

http://www.jianshu.com/p/f1208b5e42d9git

CocoaChina 對應地址:
http://www.cocoachina.com/ios/20141202/10390.htmlgithub

2014年12月1日swift

2015年4月15日,更新至Xcode6.3及Swift1.2api

感謝nevermosby對本文的遺漏之處進行指正,同時也感謝SwiftLanguage的推薦數組


學習如何使用 Alamofire 來輕鬆地實現 Swift 的網絡請求處理
學習如何使用 Alamofire 來輕鬆地實現 Swift 的網絡請求處理

AFNetworking是 iOS 和 OS X 上最受歡迎的第三方庫之一。它曾在咱們的2012年的讀者評選中榮獲2012年度最佳 iOS 庫稱號。它一樣也在 Github 上面得到了14000多個 stars 和4000多個 forks,是使用最廣的開源項目之一。xcode

最近,AFNetworking 的做者 Mattt Thompson提交了一個新的相似於 AFNetworking 的網絡 基礎庫,而且是專門使用最新的 Swift 語言來編寫的,名爲:Alamofire服務器

AFNetwork 的前綴 AF 是 Alamofire 的縮寫,所以這個新的庫名稱實際上是根據 Swift 的約定來進行命名的。網絡

在本教程的第一部分中,咱們將帶領你們使用 Alamofire 來製做一個圖片庫應用,其來源是500px.com。在這個過程當中,您能夠學習到如何使用 Alamofire 中的重要組件,以及瞭解在應用中處理網絡請求的某些重要的知識點。

本教程的第二部分將基於第一部分所製做的應用,併爲其增長一些好用的功能。您能夠經過這個過程學習到更多高級的 Alamofire 用法。

本教程假定您已熟悉 Swift 語言以及 iOS 開發。若是不是的話,請參閱咱們的其餘教程。還有,本教程使用 Xcode 6.3 做爲開發環境。

提示:

若是您已經熟悉了 Alamofire 的基本用法,那麼您能夠直接跳到本文的第二部分。可是請確保您已擁有消費者密鑰(cunsumer key),而後參照下文在應用中替換它。

讓咱們開始吧

首先下載本次教程的初始項目。這個項目中提供了在本教程中須要的所有 UI。這有助於您可以將注意力集中到學習 Alamofire 的使用上來,而不是花費大量時間來研究 UI。

在 Xcode 中打開這個項目,並定位到Main.storyboard文件:

咱們應用的主屏幕使用UITabBarController這個經常使用的 UI 樣式。咱們的標籤控制器中包含有兩個標籤,每一個標籤都有它們本身的UINavigationController頁面。第一個標籤讓用戶瀏覽熱門圖片。第二個標籤讓用戶瀏覽他們已保存的文件。兩個標籤都使用UICollectionViewController來向用戶展現圖片。故事板中一樣也包含了兩個獨立的視圖控制器,在接下來的教程中咱們將會用到它們。

生成並運行該應用,您首先會看到一個不停在加載的加載控件:

這看起來一點也不高端大氣上檔次……能夠說沒什麼可看的。可是很快咱們將會藉助 Alamofire 來讓他逼格高起來。

提示:

若是您很熟悉 AFNetworking 的使用,那麼您可能會期待下一節咱們談論 CocoaPods相關用法。藉助CocoaPods 0.36及以上版本,您即可以在iOS 8.0以上環境中使用Swift的內置框架了。

在本教程當中,咱們將會使用另一個不須要CocoaPods的方法來導入Alamofire。固然,若是您對CocoaPods很熟悉,那麼也能夠盡情地使用它來導入,這二者基本沒有任何區別。

要獲取最新版本的 Alamofire,請前往https://github.com/Alamofire/Alamofire而後單擊網頁右邊的Download ZIP按鈕。接着在 Finder 中打開起始項目文件夾,而後將Alamofire-master 文件夾拖入到您的主項目文件夾中。

打開Alamofire-master文件夾(如今它位於您的項目文件夾中),而後將Alamofire.xcodeprij文件(注意是藍色圖標!不是白色圖標!)直接拖進 Xcode 中的 Photomania 項目下面,以下圖所示:

接下來,單擊Photomania項目,進入 General 選項卡。向下滾動到Embedded Binaries項,而後單擊其下方的 + 號。選擇Alamofire.framework,最後點擊Add完成添加。

生成並運行您的項目以確保沒有任何錯誤出現,而後就能夠進入到下一節內容了。

使用 Alamofire 來檢索數據

您可能會以爲,爲何咱們要使用 Alamofire 呢?明明蘋果已經提供了NSURLSession類以及其相關類,以便讓咱們經過 HTTP 來下載相應內容。爲何咱們還要傷精費神地去使用第三方庫呢?

簡單來講,Alamofire 實際上是基於NSURLSession的,可是它能夠免去您寫樣板(boilerplate)代碼的麻煩,而且可讓網絡模塊的代碼更爲簡單易用。您能夠經過一些很是簡單的操做來訪問 Internet 上的數據,而且寫出來的代碼也會更加清晰明瞭、簡單易讀。

要使用 Alamofire 的話,首先須要導入它。請打開PhotoBrowserCollectionViewController.swift文件,而後在文件頂部添加以下代碼:

import Alamofire

您須要在每一個使用了 Alamofire 類以及函數的文件中添加這條import語句。

接下來,在setupView()下方的viewDidLoad()方法中添加以下代碼:

Alamofire.request(.GET, "https://api.500px.com/v1/photos").responseJSON() {
  (_, _, data, _) in
  println(data)
}

過會兒我會對其作出詳細解釋,可是首先您須要生成並運行該應用,這個時候您會在控制檯中看到以下信息:

Optional({
    error = "Consumer key missing.";
    status = 401;
})

您可能不明白它說了什麼鬼,不過實際上您已經成功地使用 Alamofire 來實現網絡請求了!您向 Internet 上的資源發出了一個請求,而後返回了一個JSON 數據。

下面來解釋一下那些代碼到底作了些什麼:

  • Alamofire.request(_:_)接收兩個參數:methodURLString。其中,method 一般是.GET.POSTURLString一般是您想要訪問的內容的 URL。其將返回一個Alamofire.Request對象。
  • 一般狀況下,您只需將請求對象連接到響應方法上。例如,在上面的代碼中,請求對象簡單地調用了responseJSON()方法。當網絡請求完畢後,responseJSON()方法會調用咱們所提供的閉包。在咱們的示例中,咱們只是簡單的將通過解析的 JSON 輸出到控制檯中。
  • 調用responseJSON方法意味着您指望得到一個 JSON 數據。在咱們的示例中,Alamofire 試圖解析響應數據並返回一個 JSON 對象。或者,您可使用responsePropertyList來請求得到一個屬性列表,也可使用responseString來請求得到一個初始字符串。在本教程後面,您將瞭解更多關於響應序列化方法的使用方式。

您能夠從控制檯中看到輸出的響應數據,服務器報告您須要一個名爲consumer key的東西。在咱們繼續使用 Alamofire 以前,咱們須要從 500px 網站的 API 中獲取一個密鑰。

獲取消費者密鑰

前往https://500px.com/signup,而後使用您的郵箱免費註冊,或者使用您的 Facebook 、Twitter 或者 Google 賬號登陸。

一旦您完成了註冊流程,那麼前往https://500px.com/settings/applications並單擊"Register your application"。

您會看到以下所示的對話框:

紅色大箭頭指向的那些文本框裏面的內容都是必填的。使用Alamofire Tutorial做爲Application Name,而後使用iOS App做爲Description。目前您的應用尚未Application URL,可是您能夠隨意輸一個有效的網址來完成應用註冊,您可使用raywenderlich.com^_^。

最後,在Developer’s Email中輸入您的郵箱地址,而後單擊複選框來接受使用協議。

接着,單擊 Register按鈕,您會看到一個以下所示的框:

單擊See application details連接,而後它會彈出詳細信息,這時候您就能夠定義您的消費者密鑰了,以下所示:

從該頁面中複製出您的消費者密鑰,而後返回 Xcode,而後用以下代碼替換掉目前爲止您惟一添加的代碼:

Alamofire.request(.GET, "https://api.500px.com/v1/photos", parameters: ["consumer_key": "PASTE_YOUR_CONSUMER_KEY_HERE"]).responseJSON() {
  (_, _, JSON, _) in
  println(JSON)
}

請確保您已經用複製的消費者密鑰來替換掉PASTE_YOUR_CONSUMER_KEY_HERE

生成並運行您的應用,這時您會在控制檯中看見海量的輸出:

上述全部的輸出意味着您成功地下載到了含有一些照片信息的 JSON信息。

JSON 數據中包含了一些圖片集的屬性、一些頁面信息,以及一個圖片數組。這裏是我獲得的搜索結果(您的可能會略有不一樣):

{
  "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 ...",
    .
    .
    .
    }
  ]
}

如今咱們已經擁有了 JSON 數據,接下來咱們就能夠好好地利用它了。

使用以下代碼替換掉viewDidLoad()中的 println(JSON)方法:

let 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 數據轉變爲了更易於管理的PhotoInfo數組對象。這些對象只是簡化掉了圖片 ID 和 URL 屬性的存儲桶(bucket)。您一樣能夠發現代碼過濾掉了一些……呃……您不但願出現的一些圖片。

上述代碼一樣也從新加載了集合視圖。初始項目的示例代碼基於咱們剛剛填充的photos,來建立集合視圖的單元。

生成並運行您的應用,這時加載控件加載一下子便消失。若是您仔細觀察的話,您會發現一堆灰黑色的方形單元格:

離咱們的目標愈來愈接近了,加油!

咱們仍然選擇PhotoBrowserCollectionViewController.swift文件,在collectionView(_: cellForItemAtIndexPath:)方法中的return cell前加上以下的代碼:

let 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數組中的圖片建立了另外的 Alamofire 請求。因爲這是一個圖片請求,所以咱們使用的是一個簡單的request方法,其在NSData 的blob 中返回響應。接下來咱們直接把數據放入到一個UIImage的實例中,而後反過來將實例放入早已存在於示例項目中的imageView 當中。

再一次生成並運行您的應用,這時應當出現一個圖片集合,與下圖類似:

對於 Alamofire 的工做效果想必您如今已經心中有數,可是您不會想在每次從服務器請求數據的時候,要不停的複製、粘貼 API 地址,以及添加消費者密鑰。除了這一點很是讓人不爽外,若是 API 地址發生了改變,那麼您可能不得再也不次建立一個新的消費者密鑰。

幸運的是,Alamofire對於這個問題有着良好的解決方案。

建立請求路由

打開Five100px.swift,而後找到struct Five100px,其中定義了enum ImageSize。這是一個簡單的基於 500px.com 的 API 文件的結構體。

在使用 Alamofire 以前,您須要在文件頂部添加下述的必要聲明:

import Alamofire

如今,在struct Five100px中的enum ImageSize代碼段上方添加下述代碼:

enum Router: URLRequestConvertible {
  static let baseURLString = "https://api.500px.com/v1"
  static let consumerKey = "PASTE_YOUR_CONSUMER_KEY_HERE"

    case PopularPhotos(Int)
    case PhotoInfo(Int, ImageSize)
    case Comments(Int, Int)

    var URLRequest: NSURLRequest {
      let (path: String, parameters: [String: AnyObject]) = {
        switch self {
        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):
          var params = ["consumer_key": Router.consumerKey, "image_size": "\(imageSize.rawValue)"]
          return ("/photos/\(photoID)", params)
        case .Comments(let photoID, let commentsPage):
          var params = ["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

        return encoding.encode(URLRequest, parameters: parameters).0
  }
}

這就是咱們所建立的路由,它爲咱們的 API 調用方法建立合適的URLString實例。它是一個簡單的遵照URLRequestConertible協議的enum類型,這個協議是在 Alamofire 當中定義的。當有枚舉類型採用該協議的時候,該類型就必須含有一個名爲URLRequestNSURLRequest類型變量。

這個路由含有兩個靜態常量:API 的baseURLString以及consumerKey。(最後一次聲明,請PASTE_YOUR_CONSUMER_KEY_HERE替換爲您本身的消費者密鑰)如今,這個路由能夠在必要的時候向最終的URLString中添加消費者密鑰。

您的應用擁有三個 API 終點(endpoint):一個用來取出熱門照片列表,一個用來取出某個特定照片的具體信息,一個用來取出某個照片的評論。路由將會藉助三個相應的case聲明來處理這三個終結點,每一個終結點都會接收一到兩個參數。

咱們已經定義了var URLRequest: NSURLRequest做爲計算(computed)屬性。這意味着每次咱們使用enum的時候,它都會構造出基於特定case和其參數的最終 URL。

這裏有一個示例代碼片斷,說明了上述的邏輯關係:

Five100px.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 的終結點來尋找一個 ID 爲10000的大尺寸圖片。註釋行將 URL 的結構進行了拆分。在這個示例中,URL 由三個部分組成:baseURLStringpath(?前面的那一部分)以及[String: AnyObject]字典,其中包含有傳遞給 API 終結點的參數。

對於path來講,返回元組的第一個元素能夠用如下的字符串形式返回:

"/photos/\(photoID)" // "/photos/10000"

和響應解析相似,請求參數能夠被編碼爲 JSON、屬性列表或者是字符串。一般狀況下使用簡單的字符串參數,和上面咱們所作的相似。

若是您打算在您本身的項目中使用路由,您必須對它的運行機制至關熟悉。爲此,請嘗試搞清楚要如何構造出如下的 URL:

https://api.foursquare.com/v2/users/{USER_ID}/lists?v=20131016&group=created

您是怎麼作的呢?若是您不是百分百肯定答案,請花一點時間來分析下面的代碼,直到您徹底搞明白其工做原理:

解決方案:

static let baseURLString = "https://api.foursquare.com/v2"

case UserLists(Int)

var URLRequest: NSURLRequest {
  let (path: String, parameters: [String: AnyObject]) = {
    switch self {
    case . UserLists (let userID):
      let params = ["v": "20131016", "group": "created"]
      return ("/users/\(userID)/lists", params)
    }
  }()
.
.
.

這裏您須要爲枚舉添加其餘的 case,好比說用戶列表,它們都設置有合適的參數和路徑。

加載更多圖片

好的,如今應用目前顯示的照片只有一個頁面,可是咱們想要瀏覽更多照片以找到咱們心儀的內容。多多益善,對吧?

打開PhotoBrowserCollectionViewController.swift,而後在let refreshControl = UIRefreshControl()語句下方添加以下代碼:

var populatingPhotos = false
var currentPage = 1

這裏咱們定義了兩個變量,來記錄當前是否在更新照片,以及當前咱們正在瀏覽的是哪個照片頁面。

接下來,用如下代碼替換當前viewDidLoad()的聲明:

override func viewDidLoad() {
  super.viewDidLoad()

  setupView()

  populatePhotos()
}

這裏咱們用populatePhotos()函數來替換了先前的 Alamofire 請求。以後咱們就要實現populatePhotos()函數的聲明。

一樣的,在handleRefresh()上方添加兩個函數,以下所述:

// 1
override func scrollViewDidScroll(scrollView: UIScrollView) {
  if scrollView.contentOffset.y + view.frame.size.height > scrollView.contentSize.height * 0.8 {
    populatePhotos()
  }
}

func populatePhotos() {
  // 2
  if populatingPhotos {
    return
  }

  populatingPhotos = true

  // 3
  Alamofire.request(Five100px.Router.PopularPhotos(self.currentPage)).responseJSON() {
    (_, _, JSON, error) in

    if error == nil {
      // 4
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
        // 5, 6, 7
        let photoInfos = ((JSON as! NSDictionary).valueForKey("photos") as! [NSDictionary]).filter({ ($0["nsfw"] as! Bool) == false }).map { PhotoInfo(id: $0["id"] as! Int, url: $0["image_url"] as! String) }

        // 8
        let lastItem = self.photos.count
        // 9
        self.photos.addObjectsFromArray(photoInfos)

        // 10
        let indexPaths = (lastItem..<self.photos.count).map { NSIndexPath(forItem: $0, inSection: 0) }

        // 11
        dispatch_async(dispatch_get_main_queue()) {
          self.collectionView!.insertItemsAtIndexPaths(indexPaths)
        }

        self.currentPage++
      }
    }
    self.populatingPhotos = false
  }
}

啊……好長一段代碼,對吧?下面是對每一個註釋部分的詳細解釋:

  1. 一旦您滾動超過了 80% 的頁面,那麼scrollViewDidScroll()方法將會加載更多的圖片。
  2. populatePhotos()方法在currentPage當中加載圖片,而且使用populatingPhotos做爲標記,以防止還在加載當前界面時加載下一個頁面。
  3. 這裏咱們首次使用了咱們建立的路由。只需將頁數傳遞進去,它將爲該頁面構造 URL 字符串。500px.com 網站在每次 API 調用後返回大約50張圖片,所以您須要爲下一批照片的顯示再次調用路由。
  4. 要注意,.responseJSON()後面的代碼塊:completion handler(完成處理方法)必須在主線程運行。若是您正在執行其餘的長期運行操做,好比說調用 API,那麼您必須使用 GCD 來將您的代碼調度到另外一個隊列運行。在本示例中,咱們使用DISPATCH_QUEUE_PRIORITY_HIGH來運行這個操做。
  5. 您可能會關心 JSON 數據中的photos關鍵字,其位於數組中的字典中。每一個字典都包含有一張圖片的信息。
  6. 咱們使用 Swift 的filter函數來過濾掉 NSFW 圖片(Not Safe For Work)
  7. map函數接收了一個閉包,而後返回一個PhotoInfo對象的數組。這個類是在Five100px.swift當中定義的。若是您查看這個類的源碼,那麼就能夠看到它重寫了isEqualhash這兩個方法。這兩個方法都是用一個整型的 id 屬性,所以排序和惟一化(uniquing)PhotoInfo對象仍會是一個比較快的操做
  8. 接下來咱們會在添加新的數據前存儲圖片的當前數量,使用它來更新collectionView
  9. 若是有人在咱們滾動前向 500px.com 網站上傳了新的圖片,那麼您所得到的新的一批照片將可能會包含一部分已下載的圖片。這就是爲何咱們定義var photos = NSMutableOrderedSet()爲一個組。因爲組內的項目必須惟一,所以重複的圖片不會再次出現
  10. 這裏咱們建立了一個NSIndexPath對象的數組,並將其插入到collectionView
  11. 在集合視圖中插入項目,請在主隊列中完成該操做,由於全部的 UIKit 操做都必須運行在主隊列中

生成並運行您的應用,而後緩慢向下滑動圖片。您能夠看到新的圖片將持續加載:

不斷加快滑動的速度,注意到問題沒有?對的,滾動操做不是很穩定,有些許遲鈍的感受。這並非咱們想要提供給用戶的體驗,可是咱們在下一節中就能夠修正這個問題了。

建立自定義響應序列化方法(Serializer)

您已經看到,咱們在 Alamofire 中使用所提供的 JSON、字符串,以及屬性列表序列化方法是一件很是簡單的事情。可是有些時候,您可能會想要建立本身的自定義相應序列化。例如,您能夠寫一個響應序列化方法來直接接收UIIMage,而不是將UIImage轉化爲NSData來接收。

在本節中,您將學習如何建立自定義響應序列化方法。

打開Five100px.swift,而後在靠近文件頂部的地方,也就是import Alamofire語句下面添加以下代碼:

extension Alamofire.Request {
  class func imageResponseSerializer() -> Serializer {
    return { request, response, data in
      if data == nil {
        return (nil, nil)
      }

      let image = UIImage(data: data!, scale: UIScreen.mainScreen().scale)

      return (image, nil)
    }
  }

  func responseImage(completionHandler: (NSURLRequest, NSHTTPURLResponse?, UIImage?, NSError?) -> Void) -> Self {
    return response(serializer: Request.imageResponseSerializer(), completionHandler: { (request, response, image, error) in
      completionHandler(request, response, image as? UIImage, error)
    })
  }
}

要建立一個新的響應序列化方法,咱們首先應當須要一個類方法,其返回Serializer閉包(好比說上面所寫的imageResponseSerializer())。這個閉包是 Alamofire 中的一個別名,其接收三個參數並返回所示的兩個參數,以下所示:

public typealias Serializer = (NSURLRequest, NSHTTPURLResponse?, NSData?) -> (AnyObject?, NSError?)

類方法(例如imageResponseSerializer())接收底層的NSURLSession請求以及和響應對象一塊兒的基本NSData數據實現方法(從服務器傳來的),來做爲參數。該方法接下來使用這些對象來序列化,並將其輸入到一個有意義的數據結構中,而後將其從方法中返回,它一樣也會返回在這個過程當中發生的錯誤。在咱們的示例中,咱們使用UIImage來將數據轉化爲圖片對象。

一般狀況下,當您建立了一個響應序列化方法後,您可能還會向建立一個新的響應處理方法來對其進行處理,並讓其更加易用。咱們使用.responseImage()方法來完成這項任務。這個方法的操做很簡單:它使用completionHandler,一個以閉包形式的代碼塊。一旦咱們從服務器中序列化了數據,那麼這個代碼塊將會運行。咱們所須要作的就是在響應處理方法中調用 Alamofire 本身的通用.response()響應處理方法。

讓咱們開始讓它工做起來。打開PhotoBrowserCollectionViewController.swift,而後在PhotoBrowserCollectionViewCell中的imageView屬性下面,添加以下一個屬性:

var request: Alamofire.Request?

這個屬性會爲這個單元存儲 Alamofire 的請求來加載圖片

如今將collectionView(_: cellForItemAtIndexPath:)的內容替換爲下面所示的代碼:

let cell = collectionView.dequeueReusableCellWithReuseIdentifier(PhotoBrowserCellIdentifier, forIndexPath: indexPath) as! PhotoBrowserCollectionViewCell

let imageURL = (photos.objectAtIndex(indexPath.row) as! PhotoInfo).url

cell.imageView.image = nil
cell.request?.cancel()

cell.request = Alamofire.request(.GET, imageURL).responseImage() {
  (request, _, image, error) in
  if error == nil && image != nil {
    cell.imageView.image = image
  }
}

return cell

生成並運行您的應用,再次滾動圖片,您會發現滾動變得流暢了。

爲何會流暢呢?

那麼咱們到底作了些什麼來讓滾動變得流暢了呢?其關鍵就是collectionView(_: cellForItemAtIndexPath:)中的代碼。可是在咱們解釋這段代碼以前,咱們須要向您解釋網絡調用的異步性。

Alamofire 的網絡調用是異步請求方式。這意味着提交網絡請求不會阻止剩餘代碼的執行。網絡請求可能會執行很長時間才能獲得返回結果,可是您不會但願在等待圖片下載的時候 UI 被凍結。

也就是說,實現異步請求是一個極大的挑戰。若是在發出請求以後到從服務器接收到響應的這段時間中,UI 發生了改變的話怎麼辦?

例如,UICollectionView擁有內部的單元出列機制。建立新的單元對系統來講開銷很大,所以集合視圖將重用不在屏幕上顯示的現有單元,而不是不停建立新的單元。

這意味着同一個單元對象,會被不停地重複使用。所以在發出 Alamofire 請求以後到接收到圖片信息響應以前,用戶將單元滾動出屏幕而且刪除圖片的操做將成爲可能。單元可能會出列,而且準備顯示另外一個圖片。

在上述的代碼中,咱們完成了兩件事來解決這個問題。第一件事是,當一個單元出列後,咱們經過設值爲nil的方法來清除圖片。這個操做確保咱們不會顯示原先的圖片;第二件事是,咱們的請求完成處理方法將檢查單元的 URL 是否和請求的 URL 相等。若是不相等的話,顯然單元已經擁有了另外的圖片,那麼完成處理方法將不會浪費其生命週期來爲單元設置錯誤的圖片。

接下來該何去何從?

您能夠在這裏下載本教程第一部分的最終版本項目。

提示:

若是您打算直接使用上面的的最終版本,那麼千萬不要忘記在前面的教程中所說的,用您的消費者密鑰酌情替換Five100px.swift中的響應內容。

本教程介紹了至關多的內容,如今您能夠好好的休息一下了!如今,多虧了 Alamofire,您的應用擁有了基本的照片瀏覽功能。

在咱們的學習過程當中,您已經學會了如何使用 Alamofire 發送 GET 請求、傳遞參數、建立請求路由,甚至學會了建立您本身的響應序列化方法。

在本教程的第二部分,您將會增長如下功能:

  • 照片查看器
  • 查看評論以及其餘信息的能力
  • 下載照片的選項,附帶有一個圓列進度條
  • 下拉刷新操做

咱們但願您可以喜歡咱們這部分的教程,而且可以加入咱們的第二部分的教程。若是您對 Alamofire 有任何見解或建議,快來加入咱們的討論吧!

相關文章
相關標籤/搜索