封裝一個 Swift-Style 的網路模組

發表於2016-05-23

Swift 跟 OC 有著完全不同的設計哲學,它鼓勵你使用 protocol 而不是 super class,使用 enum 和 struct 而不是 class,它支援函式式特性、範型和型別推導,讓你可以輕鬆封裝非同步過程,用鏈式呼叫避免 callback hell。如果你還是用 OC 的思維寫著 Swift 程式碼,那可以說是一種極大的資源浪費,你可能還會因為 Swift 弱雞的反射而對它感到不滿,畢竟 Swift 在強型別和安全性方面下足了功夫,如果不使用 OC 的 runtime,在動態性方面是遠不如 OC 的。

OOP 和訊息傳遞非常適合 UI 程式設計,在這方面來說 OC 是非常稱職的,整個 Cocoa Touch 框架也都是物件導向的,所以對於 iOS 開發來說,不管你使用什麼語言,都必須熟悉 OOP。在 UI 構建方面,無論是 Swift 還是 OC,無非都是呼叫 API 罷了,在有自動提示的情況下,其實編碼體驗都差不多。那 Swift 相比於 OC 的優勢到底體現在什麼地方呢,我認為是 UI 以外的地方,跟 UI 關係越小,Swift 能一展拳腳的餘地就越大,譬如網路層。

講到網路層就繞不開 Alamofire,Alamofire 幾乎是現在用 Swift 開發 iOS App 的標配,它是個很棒的庫,幾乎能滿足所有網路方面的日常需求,但如果對它再封裝一下的話,不僅使用起來更得心應手,而且能將第三方庫與業務程式碼解耦,以後萬一要更換方案會更加方便。

Alamofire 使用 Result 來表示請求返回的結果,它是個 enum,長這樣:

我們可以對它進行擴充套件,讓它支援鏈式呼叫:

有了這個擴充套件我們就可以定義一個parseResult的方法,對返回結果進行處理,像這樣:

checkJSONDict用來處理伺服器返回的錯誤資訊,具體的處理邏輯不同專案都不一樣,主要看跟伺服器的約定,我就不細說了。valueForKey是對Dictionary的擴充套件,可以通過字串拿到返回的 JSON 資料中需要的部分(先轉換成[String: AnyObject]),支援用”.”分隔 key,從而取得巢狀物件。譬如這樣一個東西:

你可以用"key2.nest"拿到value2,用"key3.nest1.nest2"拿到value3。我用reduce實現了這個功能:

有了parseResult之後,我們就可以輕鬆封裝請求過程了:

API是一個列舉,有一個url的計算屬性,用來返回 API 地址,configParameters用來配置請求引數,也跟具體專案有關,就不展開了,method可以設定一個專案中常用的 HTTP Method 作為預設引數。這個方法會返回一個Cancellable,長這樣:

Request本來就實現了cancel方法,所以只要顯式地宣告一下它遵守Cancellable協議就行了,使用的時候像這樣:

在請求完成之前,隨時可以呼叫task?.cancel() 來取消這個網路任務。

當然如果你想在網路模組中把 JSON 直接轉化成 Model 也是可以的,我個人傾向於使用 ObjectMapper 來構建網路 Model 層,於是就可以對外提供兩個直接取得 Model 和 Model 陣列的方法:

=>是我自定義的操作符,它有兩個過載版本,都滿足flatMap的引數要求:

於是就可以在業務程式碼中直接這樣:

到此一個簡潔方便的網路模組就差不多成型了,別忘了為你的模組新增單元測試,這會讓模組的使用者對你的程式碼更有信心,而且在測試過程中會讓你發現一些開發過程中的思維盲區,還能幫你優化設計,畢竟良好的可測試性在某種程度上就意味著良好的可讀性和可維護性。

有什麼建議歡迎在評論中指出 ^ ^

相關文章