序言
以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寫一個接受完成閉包的方法。
class NetworkingManager {
static let sharedInstance = NetworkingManager()
func requestDataForMainPage(completionHandler: (mainPage: FirstPage?) -> Void) {
}
}
第四步:使用Alamofire發送網絡請求,在完成閉包里使用 JSONExport 為每個Model提供的fromDictionary方法將JSON灌進Model中,將Model傳給業務層送來的completionHandler。
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。
NetworkingManager.sharedInstance.requestDataForMainPage { (mainPage) in
if let exist = mainPage {
self.mainPage = exist
print("something")
} else {
print("nothing")
}
}
這樣既實現了網絡訪問的統一管理又避免了大段的JSON解析代碼,比起原來的方法,不知道高到哪里去了。