[譯] Alamofire Tutorial: Getting Started

[譯] Alamofire Tutorial: Getting Started

轉(zhuǎn)載請注明出處:http://leejunhui.com/2017/01/23/Alamofire-Tutorial/

本文翻譯自RaywenderlichAlamofire Tutorial: Getting Started

注:已更新到Alamofire 4, Xcode 8.2, iOS 10以及Swift 3.

Alamofire是一個為iOSmacOS打造的并基于Swift的網(wǎng)絡(luò)庫.它在Apple的基礎(chǔ)網(wǎng)絡(luò)架構(gòu)上提供了更加優(yōu)雅的接口來簡化繁重而常用的網(wǎng)絡(luò)請求任務(wù)导狡。
Alamofire提供了鏈?zhǔn)降?code>request/response方法邑蒋,JSON的傳參和響應(yīng)序列化,身份認(rèn)證和其他特性涂炎。在這篇Alamofire教程中秃流,你將使用Alamofire來執(zhí)行像從第三方提供的RESTfulapi接口上傳文件赂蕴,請求數(shù)據(jù)等基本網(wǎng)絡(luò)操作。Alamofire的優(yōu)雅之處在于它完完全全是由Swift寫成的剔应,并且沒有從它的Objective-C版本-AFNetworking那繼承任何特性睡腿。

你應(yīng)該對于HTTP網(wǎng)絡(luò)有一個概念性的理解,并且還應(yīng)該接觸過Apple的網(wǎng)絡(luò)類比如URLSession.當(dāng)然Alamofire中的一些細(xì)節(jié)會有點(diǎn)晦澀難懂峻贮,如果你有曾經(jīng)解決過網(wǎng)絡(luò)請求方面的經(jīng)驗也是極好的席怪。你還需要使用CocoaPods來將Alamofire集成到項目中。

Getting Started

下載項目代碼 摸我

下面介紹的項目名為PhotoTagger,當(dāng)我們完成該項目后纤控,可以實現(xiàn):從相冊中選擇一張照片(如果你用的是真機(jī)測試的話挂捻,可以拍取一張照片),然后上傳這張照片到一個第三方平臺船万,平臺會對該照片進(jìn)行圖像識別刻撒,然后返回一個tag的列表和這張圖片的主色。

img
img

編譯并運(yùn)行該項目耿导,你就可以看到如下界面:

img
img

點(diǎn)擊Select Photo声怔,然后選擇一張照片,接著界面背景圖就會變成你選擇的那張照片舱呻。打開Main.stroyboard,顯示tags和colors的界面已經(jīng)為你準(zhǔn)備好了醋火。剩下的工作就是上傳照片和獲取tags以及colors了。

The Imagga API

Imagga是一個為開發(fā)者和企業(yè)提供構(gòu)建可伸縮的箱吕,以圖片為主的云產(chǎn)品的圖像識別服務(wù)的網(wǎng)站芥驳。你可以先嘗試下官方的自動標(biāo)記服務(wù)demo:地址.

你需要再Imagga為本文的項目創(chuàng)建一個免費(fèi)的開發(fā)者賬號。因為Imagga對于每個HTTP請求都進(jìn)行了權(quán)限認(rèn)證茬高,所以只有擁有該網(wǎng)站賬號的用戶才可以使用他們的服務(wù)兆旬。來到 https://imagga.com/auth/signup/hacker,然后注冊怎栽。完成注冊后宿饱,檢查下是否如下圖所示:

img
img

Authorization區(qū)域列出了一個等會你將用到的token。該token將會被包含在每個發(fā)往Imagga的請求的頭部宝鼓。

提示:請確認(rèn)你拷貝了整個token字符串刑棵,確認(rèn)滑動了最右邊并檢查是否拷貝完整。

稍后你將會用到Imagga的api來上傳圖片愚铡,taggingapi實現(xiàn)圖像識別蛉签,colorsapi用來顏色識別。你可以在http://docs.imagga.com上查看所有的api沥寥。

Installing Dependencies 安裝依賴

在項目主目錄里創(chuàng)建Podfile文件碍舍,內(nèi)容如下:

platform :ios, '10.0'

inhibit_all_warnings!
use_frameworks!
 
target 'PhotoTagger' do
  pod 'Alamofire', '~> 4.2.0'
end

然后打開終端,來到項目主目錄下邑雅,執(zhí)行pod install命令片橡。如果你的Mac上沒有安裝過CocoaPods的話,可以查看How to Use CocoaPods with Swift教程淮野。

確保你使用的是CocoaPods的最新版本捧书,否則可能會在安裝第三方庫時報錯。本文撰寫時骤星,最新版本為1.1.1经瓷。

關(guān)閉Xcode項目,然后打開新生成的PhotoTagger.xcworkspace文件洞难。編譯然后運(yùn)行舆吮,跑起來的效果應(yīng)該和之前保持一致。你下一步的任務(wù)是添加一些HTTP請求來從RESTful服務(wù)獲取一些JSON數(shù)據(jù)队贱。

REST, HTTP, JSON — 是什么呢?

如果你對使用HTTP不是特別有經(jīng)驗的話色冀,那么你可能會很好奇這些縮寫詞到底是什么含義。
HTTP是一種應(yīng)用層協(xié)議柱嫌,或者可以理解為一套網(wǎng)站用來從web服務(wù)器傳輸數(shù)據(jù)到你的電腦屏幕上的規(guī)范锋恬。你應(yīng)該看到了你在瀏覽器中所輸入的每個URL前面都會有HTTPHTTPS作為前綴。你可能也聽過其它的應(yīng)用層協(xié)議编丘,比如FTP,TelnetSSH与学。HTTP定義了一些客戶端(你的瀏覽器或者app)用來指明所需的行為,請求的方法或者說動作:
GET: 獲取數(shù)據(jù)瘪吏,比如一個WEB頁面。但是不更改服務(wù)器上的任何內(nèi)容蜗巧。
HEAD: 與GET相同掌眠,但是服務(wù)器只會返回頭部,并不會返回實際的數(shù)據(jù)幕屹。
POST: 發(fā)送數(shù)據(jù)到服務(wù)器蓝丙,通常用于表單提交级遭。
PUT: 發(fā)送數(shù)據(jù)到指定的路徑。
DELETE: 刪除指定路徑的數(shù)據(jù)渺尘。

REST, 或者表征狀態(tài)轉(zhuǎn)移挫鸽,是用來設(shè)計可持續(xù)的,易于使用的以及易于維護(hù)的WEB API的一套規(guī)范鸥跟。REST有幾個體系結(jié)構(gòu)規(guī)則丢郊,用于強(qiáng)制執(zhí)行某些操作,例如不在請求之間保持狀態(tài)医咨,使請求可緩存枫匾,并提供統(tǒng)一的接口。這樣拟淮,像您這樣的應(yīng)用開發(fā)者就可以輕松地將API集成到您的應(yīng)用中干茉,而無需跟蹤請求之間的數(shù)據(jù)狀態(tài)。
JSON代表JavaScript Object Notation; 它提供了一個用于在兩個系統(tǒng)之間用于傳輸數(shù)據(jù)的直接的很泊,人類可讀的和便攜的機(jī)制角虫。JSON的數(shù)據(jù)類型有:string,boolean委造,array戳鹅,object / dictionary,null和number; 整數(shù)和小數(shù)之間沒有區(qū)別争涌。Apple提供JSONSerialization類來幫助將內(nèi)存中的對象轉(zhuǎn)換為JSON粉楚,反之亦然。

HTTP亮垫,RESTJSON的組合構(gòu)成了作為開發(fā)人員可用的Web服務(wù)的很好的一部分模软。試圖理解每個細(xì)枝末節(jié)如何工作可能是令人難以應(yīng)對的。 像Alamofire這樣的庫可以幫助減少使用這些服務(wù)的復(fù)雜性饮潦,并且在沒有幫助的情況下讓您的運(yùn)行速度更快燃异。

Alamofire適合做什么?

你為什么需要Alamofire继蜡?蘋果已經(jīng)提供了URLSession和其他類來通過HTTP獲取內(nèi)容回俐,所以為什么要使用一個第三方庫來增加復(fù)雜度呢?
簡單的答案是Alamofire是基于URLSession開發(fā)而成的稀并,但它可以讓你免于編寫樣板代碼仅颇,使編寫網(wǎng)絡(luò)代碼更容易。你可以花費(fèi)很少的精力就可以讓從網(wǎng)絡(luò)上獲取數(shù)據(jù)的操作代碼變得更加簡潔和易于閱讀碘举。
Alamofire有幾個主要功能:

  • .upload:以multipart忘瓦,流,文件或數(shù)據(jù)方法上傳文件引颈。
  • .download:下載文件或恢復(fù)正在進(jìn)行的下載耕皮。
  • .request:每個不與文件傳輸相關(guān)聯(lián)的HTTP請求境蜕。

這些Alamofire函數(shù)作用于模塊膨俐,而不是類或結(jié)構(gòu)體设拟。 Alamofire的基礎(chǔ)部分是類和結(jié)構(gòu)體变秦,如SessionManager最筒,DataRequestDataResponse; 但是捕虽,您不需要完全了解Alamofire的整個結(jié)構(gòu)即可開始使用它付魔。
下面是使用Apple的URLSession和Alamofire的請求函數(shù)進(jìn)行相同網(wǎng)絡(luò)操作的示例:

// With URLSession
public func fetchAllRooms(completion: @escaping ([RemoteRoom]?) -> Void) {
  let url = URL(string: "http://localhost:5984/rooms/_all_docs?include_docs=true")!
 
  var urlRequest = URLRequest(
    url: url,
    cachePolicy: .reloadIgnoringLocalAndRemoteCacheData,
    timeoutInterval: 10.0 * 1000)
  urlRequest.httpMethod = "GET"
  urlRequest.addValue("application/json", forHTTPHeaderField: "Accept")
 
  let task = urlSession.dataTask(with: urlRequest)
  { (data, response, error) -> Void in
    guard error == nil else {
      print("Error while fetching remote rooms: \(error)")
      completion(nil)
      return
    }

guard let data = data,
  let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
    print("Nil data received from fetchAllRooms service")
    completion(nil)
    return
}

guard let rows = json["rows"] as? [[String: Any]] else {
  print("Malformed data received from fetchAllRooms service")
  completion(nil)
  return
}

let rooms = rows.flatMap({ (roomDict) -> RemoteRoom? in
  return RemoteRoom(jsonData: roomDict)
})

completion(rooms)
  }
 
  task.resume()
}

對比:

// With Alamofire
func fetchAllRooms(completion: @escaping ([RemoteRoom]?) -> Void) {
  Alamofire.request(
    URL(string: "http://localhost:5984/rooms/_all_docs")!,
    method: .get,
    parameters: ["include_docs": "true"])
  .validate()
  .responseJSON { (response) -> Void in
    guard response.result.isSuccess else {
      print("Error while fetching remote rooms: \(response.result.error)")
      completion(nil)
      return
    }

guard let value = response.result.value as? [String: Any],
  let rows = value["rows"] as? [[String: Any]] else {
    print("Malformed data received from fetchAllRooms service")
    completion(nil)
    return
}

let rooms = rows.flatMap({ (roomDict) -> RemoteRoom? in
  return RemoteRoom(jsonData: roomDict)
})

completion(rooms)
  }
}

你可以看到Alamofire所需要的設(shè)置更加簡單愤兵,函數(shù)可讀性也更高铁蹈。你使用responseJSON(options:completionHandler:)來反序列化相應(yīng)結(jié)果舟舒,然后調(diào)用validate()方法來簡化錯誤處理±現(xiàn)在是時候來實踐使用Alamofire了。

上傳文件

打開ViewController.swift文件秃励,然后在頂部添加以下代碼:

import Alamofire

這使你可以在代碼中使用Alamofire模塊提供的功能氏仗,然后,在該文件中末尾處添加以下代碼:

// Networking calls
extension ViewController {
  func upload(image: UIImage,
              progressCompletion: @escaping (_ percent: Float) -> Void,
              completion: @escaping (_ tags: [String], _ colors: [PhotoColor]) -> Void) {
    guard let imageData = UIImageJPEGRepresentation(image, 0.5) else {
      print("Could not get JPEG representation of UIImage")
      return
    }
  }
}

將圖片上傳到Imagga的第一步是將圖片轉(zhuǎn)換為適用于API的正確格式夺鲜。上面的圖片選擇方法會返回一個轉(zhuǎn)化成JPEG格式的圖片實例皆尔。
然后,來到imagePickerController(_:didFinishPickingMediaWithInfo:)方法币励,然后在你設(shè)置imageView的后面添加以下代碼:

// 1
takePictureButton.isHidden = true
progressView.progress = 0.0
progressView.isHidden = false
activityIndicatorView.startAnimating()
 
upload(
  image: image,
  progressCompletion: { [unowned self] percent in
    // 2
    self.progressView.setProgress(percent, animated: true)
  },
  completion: { [unowned self] tags, colors in
    // 3
    self.takePictureButton.isHidden = false
    self.progressView.isHidden = true
    self.activityIndicatorView.stopAnimating()

self.tags = tags
self.colors = colors

// 4
self.performSegue(withIdentifier: "ShowResults", sender: self)
})

Alamofire的一切行為都是異步的慷蠕,這意味著你將以異步的方式更新UI。

1.隱藏上傳按鈕食呻,并顯示進(jìn)度條和活動視圖流炕。
2.當(dāng)文件上傳時,你可以調(diào)用進(jìn)度回調(diào)來獲取實時的進(jìn)度百分比仅胞,然后可以更新進(jìn)度條上的進(jìn)度每辟。
3.當(dāng)上傳完成后將執(zhí)行完成處理回調(diào),控件的狀態(tài)也將會恢復(fù)到初始狀態(tài)干旧。
4.最后渠欺,在一個成功或者失敗的上傳后進(jìn)入結(jié)果界面,用戶界面不會根據(jù)錯誤情況變化椎眯。

然后挠将,我們回到upload(image:progressCompletion:completion:)方法,然后在轉(zhuǎn)化UIImage實例代碼后面添加如下代碼:

Alamofire.upload(
  multipartFormData: { multipartFormData in
    multipartFormData.append(imageData,
                             withName: "imagefile",
                             fileName: "image.jpg",
                             mimeType: "image/jpeg")
  },
  to: "http://api.imagga.com/v1/content",
  headers: ["Authorization": "Basic xxx"],
  encodingCompletion: { encodingResult in
  }
)

請確保從Imagga網(wǎng)站上獲取到你自己的Basic授權(quán)令牌编整,然后替換掉代碼中Basic xxx的部分舔稀。這里你將JPEG數(shù)據(jù)轉(zhuǎn)換為MIME multipart請求并發(fā)送到Imagga內(nèi)容服務(wù)api。
下一步掌测,添加如下代碼:

switch encodingResult {
case .success(let upload, _, _):
  upload.uploadProgress { progress in
    progressCompletion(Float(progress.fractionCompleted))
  }
  upload.validate()
  upload.responseJSON { response in
  }
case .failure(let encodingError):
  print(encodingError)
}

這兩段代碼通過調(diào)用Alamofireupload方法内贮,然后隨著文件上傳,通過一個簡單的計算來更新進(jìn)度條UI。之后驗證響應(yīng)的狀態(tài)代碼是否在默認(rèn)可接受的范圍內(nèi)(在200和299之間)贺归。

注意:在Alamofire 4之前,不能保證總是在主隊列上調(diào)用進(jìn)度回調(diào)断箫。而從Alamofire 4開始拂酣,新的進(jìn)度回調(diào)API則總是在主隊列上調(diào)用。

接下來仲义,在upload.responseJSON中添加以下代碼:

// 1.
guard response.result.isSuccess else {
  print("Error while uploading file: \(response.result.error)")
  completion([String](), [PhotoColor]())
  return
}
 
// 2.
guard let responseJSON = response.result.value as? [String: Any],
  let uploadedFiles = responseJSON["uploaded"] as? [[String: Any]],
  let firstFile = uploadedFiles.first,
  let firstFileID = firstFile["id"] as? String else {
    print("Invalid information received from service")
    completion([String](), [PhotoColor]())
    return
}
 
print("Content uploaded with ID: \(firstFileID)")
 
// 3.
completion([String](), [PhotoColor]())

下面是上面代碼每個步驟的解釋:
1.檢查響應(yīng)是否成功婶熬,如果失敗,打印錯誤并調(diào)用完成回調(diào)函數(shù)埃撵。
2.檢查響應(yīng)的每個部分赵颅,并驗證預(yù)期的類型是否就是接收到的實際類型,然后從響應(yīng)中檢索firstFileID暂刘,如果firstFileID無法解析饺谬,則輸出錯誤信息并調(diào)用完成回調(diào)函數(shù)。
3.調(diào)用完成回調(diào)函數(shù)來更新UI谣拣。此時募寨,你并沒有下載到任何標(biāo)簽和顏色數(shù)據(jù),所以只需要傳入空數(shù)據(jù)即可森缠。

注意:每個響應(yīng)都有一個帶有值和類型的Result枚舉拔鹰。 使用自動驗證,當(dāng)結(jié)果返回200到299之間的有效HTTP代碼并且內(nèi)容類型是在Accept HTTP header字段中指定的有效類型時贵涵,將認(rèn)為結(jié)果成功列肢。
你可以通過如下所示的添加.validate參數(shù)來使用手動驗證響應(yīng)結(jié)果:

   Alamofire.request("https://httpbin.org/get", parameters: ["foo": "bar"])
  .validate(statusCode: 200..<300)
  .validate(contentType: ["application/json"])
  .response { response in
  // response handling code
}

如果你在上傳中發(fā)生錯誤的話,UI界面是不會提示錯誤信息的宾茂,它僅僅告訴用戶沒有顏色和標(biāo)簽返回瓷马。這不是最好的用戶體驗,但是對于這篇教程來說已經(jīng)足夠了刻炒。
編譯并運(yùn)行整個項目决采,選擇一張圖片然后靜觀其變。進(jìn)度條漸進(jìn)式的前進(jìn)坟奥,你應(yīng)該可以在控制臺看到如下輸出:

img
img

恭喜你树瞭,你已經(jīng)成功的通過網(wǎng)絡(luò)上傳了一張圖片了。

img
img

獲取數(shù)據(jù)

將圖片上傳到Imagga之后爱谁,下一步則是從Imagga獲取到它分析之后的標(biāo)簽數(shù)據(jù)了晒喷。在ViewController文件的擴(kuò)展方法upload(image:progress:completion:):下添加以下代碼:

func downloadTags(contentID: String, completion: @escaping ([String]) -> Void) {
  Alamofire.request(
    "http://api.imagga.com/v1/tagging",
    parameters: ["content": contentID],
    headers: ["Authorization": "Basic xxx"]
  )
  .responseJSON { response in
    guard response.result.isSuccess else {
      print("Error while fetching tags: \(response.result.error)")
      completion([String]())
      return
    }

guard let responseJSON = response.result.value as? [String: Any] else {
  print("Invalid tag information received from the service")
  completion([String]())
  return
}

print(responseJSON)
completion([String]())
  }
}

請再一次確認(rèn)替換的Basic xxx是你自己賬號的授權(quán)token。上述代碼執(zhí)行了一個對標(biāo)簽服務(wù)的GET請求访敌,參數(shù)是通過上傳成功后返回的一個ID凉敲。接著,我們回到upload(image:progress:completion:)方法,然后替換成功條件下的完成回調(diào)方法代碼如下:

self.downloadTags(contentID: firstFileID) { tags in
  completion(tags, [PhotoColor]())
}

上述代碼將獲得到的標(biāo)簽數(shù)據(jù)通過完成回調(diào)函數(shù)返回給上級調(diào)用者爷抓。
編譯然后運(yùn)行整個項目势决,上傳照片然后控制臺會打印如下結(jié)果:


img
img

本教程里你不用在意返回結(jié)果里的confidence score是什么意思,只需要關(guān)心標(biāo)簽名稱的數(shù)組即可蓝撇。下一步果复,回到downloadTags(contentID:completion:)方法,然后替換里面的.responseJSON方法渤昌,代碼如下:

// 1.
guard response.result.isSuccess else {
  print("Error while fetching tags: \(response.result.error)")
  completion([String]())
  return
}
 
// 2.
guard let responseJSON = response.result.value as? [String: Any],
  let results = responseJSON["results"] as? [[String: Any]],
  let firstObject = results.first,
  let tagsAndConfidences = firstObject["tags"] as? [[String: Any]] else {
    print("Invalid tag information received from the service")
    completion([String]())
    return
}
 
// 3.
let tags = tagsAndConfidences.flatMap({ dict in
  return dict["tag"] as? String
})
 
// 4.
completion(tags)

我們來逐步分解上面的代碼:
1.檢查返回結(jié)果是否成功虽抄,如果失敗,打印錯誤信息独柑,然后調(diào)用完成回調(diào)函數(shù)迈窟。
2.檢查返回結(jié)果的每個部分,驗證數(shù)據(jù)類型是否正確忌栅,從返回結(jié)果中獲取tagsAndConfidences车酣,如果解析失敗,打印錯誤信息索绪,然后調(diào)用完成回調(diào)函數(shù)骇径。
3.遍歷tagsAndConfidences數(shù)組里的每個字典對象,從中以tag作為key來提取對應(yīng)的值者春。
4.將從服務(wù)器獲取的標(biāo)簽數(shù)據(jù)返回給上層調(diào)用者破衔。

注意:代碼里使用Swift的flatMap方法來遍歷數(shù)組里面的每個字典。這個方法在遍歷到nil的值得時候并不會讓程序crash掉钱烟,而是直接將這些nil值移除掉晰筛,然后只返回正確的結(jié)果。所以通過使用可選解包(as?)來驗證字典中的值是否可以被轉(zhuǎn)化成為字符串拴袭。
編譯然后運(yùn)行整個項目读第,上傳一張圖片,然后你可以在界面上看到如下效果:

img
img

縱享絲滑拥刻,Imagga不愧是一個智能的api怜瞒。下一步,你將要獲取的是圖片的顏色般哼。添加如下代碼到ViewController中的downloadTags(contentID:completion:):方法下面:

 func downloadColors(contentID: String, completion: @escaping ([PhotoColor]) -> Void) {
  Alamofire.request(
    "http://api.imagga.com/v1/colors",
    parameters: ["content": contentID],
    // 1.
    headers: ["Authorization": "Basic xxx"]
  )
  .responseJSON { response in
    // 2.
    guard response.result.isSuccess else {
      print("Error while fetching colors: \(response.result.error)")
      completion([PhotoColor]())
      return
    }

// 3.
guard let responseJSON = response.result.value as? [String: Any],
  let results = responseJSON["results"] as? [[String: Any]],
  let firstResult = results.first,
  let info = firstResult["info"] as? [String: Any],
  let imageColors = info["image_colors"] as? [[String: Any]] else {
    print("Invalid color information received from service")
    completion([PhotoColor]())
    return
}

// 4.
let photoColors = imageColors.flatMap({ (dict) -> PhotoColor? in
  guard let r = dict["r"] as? String,
    let g = dict["g"] as? String,
    let b = dict["b"] as? String,
    let closestPaletteColor = dict["closest_palette_color"] as? String else {
      return nil
  }

  return PhotoColor(red: Int(r),
                    green: Int(g),
                    blue: Int(b),
                    colorName: closestPaletteColor)
})

// 5.
completion(photoColors)
  }
}

下面按照編號來依次解析:
1.確保Basic xxx是你自己賬號所屬的認(rèn)證token吴汪。
2.檢查返回結(jié)果是否成功,如果失敗蒸眠,打印錯誤信息漾橙,然后調(diào)用完成回調(diào)函數(shù)。
3.檢查返回結(jié)果的每個部分楞卡,驗證數(shù)據(jù)類型是否正確霜运。從返回結(jié)果中獲取imageColors脾歇,如果解析失敗,打印錯誤信息淘捡,然后調(diào)用完成回調(diào)函數(shù)藕各。
4.再次使用flatMap方法,對服務(wù)器返回的PhotoColors對象進(jìn)行遍歷焦除,將里面的符合RGB格式的數(shù)據(jù)轉(zhuǎn)換字符串座韵,然后封裝成PhotoColor對象。
5.調(diào)用完成回調(diào)函數(shù)踢京,傳入服務(wù)器返回的圖片顏色數(shù)據(jù)。

最后宦棺,回到upload(image:progress:completion:)方法瓣距,然后替換掉成功條件下的調(diào)用完成回調(diào)函數(shù):

self.downloadTags(contentID: firstFileID) { tags in
  self.downloadColors(contentID: firstFileID) { colors in
    completion(tags, colors)
  }
}

上述代碼嵌套了?上傳圖片,獲取標(biāo)簽以及獲取顏色的操作代咸。
編譯然后運(yùn)行整個項目蹈丸,這一次當(dāng)你選擇Colors按鈕后,界面會有以下效果:

img
img

這塊主要使用了映射到PhotoColor結(jié)構(gòu)體的RGB顏色來渲染UI呐芥。你已經(jīng)成功的往Imagga上傳了一張圖片逻杖,以及調(diào)用了2個不同的api來獲取數(shù)據(jù)。你已經(jīng)做得很不錯了思瘟,但是在如何使用Alamofire上荸百,我們還有改進(jìn)的余地。

改進(jìn) PhotoTagger

你應(yīng)該注意到了我們前面的代碼中有許多重復(fù)代碼滨攻。如果Imagga官方宣布廢除掉v1版本的api够话,并推出v2版本的api。我們的應(yīng)用將不再可用直到你將每個方法里面的URL都修改過來光绕。同樣的道理女嘲,如果你的Basic認(rèn)證token發(fā)生了變化,那么你需要在所有用到的地方做出修改诞帐。
Alamofire提供了一個簡單的方法來消除代碼重復(fù)的問題并提供了集中式的配置方法欣尼。該技術(shù)涉及創(chuàng)建符合URLRequestConvertible協(xié)議的結(jié)構(gòu)體,然后更新的上傳和請求的方法停蕉。
創(chuàng)建一個新的Swfit文件愕鼓,命名為ImaggaRouter.swift,然后在文件里替換成以下代碼:

import Foundation
import Alamofire
 
public enum ImaggaRouter: URLRequestConvertible {
  static let baseURLPath = "http://api.imagga.com/v1"
  static let authenticationToken = "Basic xxx"
 
  case content
  case tags(String)
  case colors(String)
 
  var method: HTTPMethod {
    switch self {
    case .content:
      return .post
    case .tags, .colors:
      return .get
    }
  }
 
  var path: String {
    switch self {
    case .content:
      return "/content"
    case .tags:
      return "/tagging"
    case .colors:
      return "/colors"
    }
  }

  public func asURLRequest() throws -> URLRequest {
    let parameters: [String: Any] = {
      switch self {
      case .tags(let contentID):
        return ["content": contentID]
      case .colors(let contentID):
        return ["content": contentID, "extract_object_colors": 0]
      default:
        return [:]
      }
    }()

let url = try ImaggaRouter.baseURLPath.asURL()

var request = URLRequest(url: url.appendingPathComponent(path))
request.httpMethod = method.rawValue
request.setValue(ImaggaRouter.authenticationToken, forHTTPHeaderField: "Authorization")
request.timeoutInterval = TimeInterval(10 * 1000)

return try URLEncoding.default.encode(request, with: parameters)
  }
}

替換Basic xxx為你自己賬號的認(rèn)證Token慧起。這個路由類通過提供三個不同的分類:.content, .tags(String),.colors(String)來實現(xiàn)創(chuàng)建多個URLRequest實例【軉現(xiàn)在你的重復(fù)代碼都集中到了一個地方,如果有需要在這里更改即可完慧。
回到ViewController.swift文件谋旦,然后替換upload(image:progress:completion:)方法:

Alamofire.upload(
  multipartFormData: { multipartFormData in
    multipartFormData.append(imageData,
                             withName: "imagefile",
                             fileName: "image.jpg",
                             mimeType: "image/jpeg")
  },
  to: "http://api.imagga.com/v1/content",
  headers: ["Authorization": "Basic xxx"],

替換為:

Alamofire.upload(
  multipartFormData: { multipartFormData in
  multipartFormData.append(imageData,
                           withName: "imagefile",
                           fileName: "image.jpg",
                           mimeType: "image/jpeg")
  },
  with: ImaggaRouter.content,

然后替換掉downloadTags(contentID:completion:)方法里的代碼:

Alamofire.request(ImaggaRouter.tags(contentID))

最后剩失,替換掉downloadColors(contentID:completion:)方法里的代碼:

Alamofire.request(ImaggaRouter.colors(contentID))

編譯然后運(yùn)行項目,結(jié)果應(yīng)與之前的效果保持一致册着。這意味著你已經(jīng)在不破壞你的app的前提下完成了重構(gòu)拴孤,干得漂亮!

接下來做什么甲捏?

所有的代碼文件都上傳到了github上演熟,不要忘了替換你自己的Basic認(rèn)證token。
這篇教程只涵蓋到了非乘径伲基礎(chǔ)的知識點(diǎn)芒粹。你可以去Alamofire的官方網(wǎng)站https://github.com/Alamofire/Alamofire上進(jìn)行更深入的學(xué)習(xí)。
進(jìn)一步的話大溜,你可以花一些時間來學(xué)習(xí)Alamofire底層使用的AppleURLSession類的內(nèi)容:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末化漆,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子钦奋,更是在濱河造成了極大的恐慌座云,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件付材,死亡現(xiàn)場離奇詭異朦拖,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)厌衔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門璧帝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人富寿,你說我怎么就攤上這事裸弦。” “怎么了作喘?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵理疙,是天一觀的道長。 經(jīng)常有香客問我泞坦,道長窖贤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任贰锁,我火速辦了婚禮赃梧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘豌熄。我一直安慰自己授嘀,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布锣险。 她就那樣靜靜地躺著蹄皱,像睡著了一般览闰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上巷折,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天压鉴,我揣著相機(jī)與錄音,去河邊找鬼锻拘。 笑死油吭,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的署拟。 我是一名探鬼主播婉宰,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼推穷!你這毒婦竟也來了心包?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤缨恒,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后轮听,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體骗露,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年血巍,在試婚紗的時候發(fā)現(xiàn)自己被綠了萧锉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡述寡,死狀恐怖柿隙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鲫凶,我是刑警寧澤禀崖,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站螟炫,受9級特大地震影響波附,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜昼钻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一掸屡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧然评,春花似錦仅财、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽抖锥。三九已至,卻和暖如春风喇,著一層夾襖步出監(jiān)牢的瞬間宁改,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工魂莫, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留还蹲,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓耙考,卻偏偏與公主長得像谜喊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子倦始,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

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