[譯] Alamofire Tutorial: Getting Started
轉(zhuǎn)載請注明出處:http://leejunhui.com/2017/01/23/Alamofire-Tutorial/
注:已更新到Alamofire 4
, Xcode 8.2
, iOS 10
以及Swift 3
.
Alamofire
是一個為iOS
和macOS
打造的并基于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í)行像從第三方提供的RESTful
api接口上傳文件赂蕴,請求數(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的列表和這張圖片的主色。
編譯并運(yùn)行該項目耿导,你就可以看到如下界面:
點(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,然后注冊怎栽。完成注冊后宿饱,檢查下是否如下圖所示:
在Authorization
區(qū)域列出了一個等會你將用到的token。該token將會被包含在每個發(fā)往Imagga的請求的頭部宝鼓。
提示:請確認(rèn)你拷貝了整個token字符串刑棵,確認(rèn)滑動了最右邊并檢查是否拷貝完整。
稍后你將會用到Imagga的api來上傳圖片愚铡,tagging
api實現(xiàn)圖像識別蛉签,colors
api用來顏色識別。你可以在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前面都會有HTTP
或HTTPS
作為前綴。你可能也聽過其它的應(yīng)用層協(xié)議编丘,比如FTP
,Telnet
和SSH
与学。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
亮垫,REST
和JSON
的組合構(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
最筒,DataRequest
和DataResponse
; 但是捕虽,您不需要完全了解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)用Alamofire
的upload
方法内贮,然后隨著文件上傳,通過一個簡單的計算來更新進(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)該可以在控制臺看到如下輸出:
恭喜你树瞭,你已經(jīng)成功的通過網(wǎng)絡(luò)上傳了一張圖片了。
獲取數(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é)果:
本教程里你不用在意返回結(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)行整個項目读第,上傳一張圖片,然后你可以在界面上看到如下效果:
縱享絲滑拥刻,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按鈕后,界面會有以下效果:
這塊主要使用了映射到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
底層使用的Apple
的URLSession
類的內(nèi)容: