序言
以Swift為iOS開發入門語言的新手,在網路程式設計時往往容易存在以下兩個問題:
- 沒有網路層,網路請求的程式碼散落在各處,難以統一管理,難以維護。
- 根據JSON手寫Model,採用SwiftyJSON這種半自動的方式進行JSON解析,再手工將經SwiftyJSON處理的半成品資料灌入Model之中,效率極其低下,程式碼冗餘繁雜。
首先我會提出一個網路層設計方案,之後是一個基於該設計的完整Swift網路程式設計實踐。
沒耐心看漁的可以直接看最後的魚。
網路層設計
在設計之前,我參考了@反革命攻城獅 的 iOS應用架構談 網路層設計方案
他設計的網路層包括兩部分: Manager 和 Reformer。
每個業務層擁有自己的Manager例項和Reformer例項,Manager例項負責傳送請求,取得JSON資料,JSON經Reformer處理後,採用Delegate的方式通知呼叫的業務層。
我的設計只包含Manager,而且各個業務層不擁有自己的Manager例項,Manager類本身提供一個單例,單例的例項方法是對各個API的呼叫,每個方法對應一個API,業務層將completionHandler傳給Manager單例的相應方法,該方法發出網路請求,並對得到的JSON進行解析,將解析完得到的Model傳回業務層。
設計上,我把他講的回撥時不用block,不要在Manager內解析JSON這兩條都違背了..,關於回撥時用不用block,因為我採用的是單例,而且我不想將JSON解析下放給業務層,如果硬要採用Delegate,那我就不得不為每個業務層提供一個專門的Delegate來為其進行JSON解析,如果有N個業務層,我需要先定義N個Protocol,再為Manager增加N個變數,同時還需要使N個業務層接受單例的委託,這是不現實的,如果採用block,所需要做的就只是在相應方法內為該block新增上JSON解析的程式碼然後傳給Alamofire就可以了。關於為什麼在Manager內解析JSON了,我覺得這一行程式碼的問題.. 就不用給業務層了吧。
最佳實踐嘗試
OC傳統的JSON解析方式是利用外掛或其他工具依照JSON生成Model的程式碼,然後將得到的JSON利用YYModel或MJExtension之類的第三方庫一行注入到Model中,的確比文章開頭提到的方法簡單多了,但雖然MJExtension這類的第三方庫支援Swift,但我在嘗試使用的時候遇到了NSArray和Array不相容的問題,可能是我的配置有問題,但我找到了其他可以替代而且更簡單的方式,就是用JSONExport。
JSONExport可以根據JSON為你生成對應的Model程式碼,並且也迭代的幫你寫好了JSON轉Model的程式碼,即Model的 formDictionary 方法。
魚
第一步:https://github.com/Ahmed-Ali/JSONExport 下一個JSONExport
第二步:將JSON複製進去
得到三個Model檔案,拖進你的工程,選中copyIfNeeded
第三步:依照之前的網路層設計,建一個Manager,提供一個單例,併為每個API寫一個接受完成閉包的方法。
1 2 3 4 5 6 7 |
class NetworkingManager { static let sharedInstance = NetworkingManager() func requestDataForMainPage(completionHandler: (mainPage: FirstPage?) -> Void) { } } |
第四步:使用Alamofire傳送網路請求,在完成閉包裡使用 JSONExport 為每個Model提供的fromDictionary方法將JSON灌進Model中,將Model傳給業務層送來的completionHandler。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class NetworkingManager { static let sharedInstance = NetworkingManager() func requestDataForMainPage(completionHandler: (mainPage: FirstPage?) -> Void) { Alamofire.request(.GET, "http://news-at.zhihu.com/api/4/news/latest").responseJSON(options: .AllowFragments) { response in guard let json = response.result.value else { print("Error occur") completionHandler(mainPage: nil) return } let model = FirstPage(fromDictionary: json as! NSDictionary) completionHandler(mainPage: model) } } |
第五步:業務層的程式碼通過呼叫單例的對應函式訪問相應API,並直接獲得該API所對應的Model。
1 2 3 4 5 6 7 8 |
NetworkingManager.sharedInstance.requestDataForMainPage { (mainPage) in if let exist = mainPage { self.mainPage = exist print("something") } else { print("nothing") } } |
這樣既實現了網路訪問的統一管理又避免了大段的JSON解析程式碼,比起原來的方法,不知道高到哪裡去了。