序言
以Swift為iOS開(kāi)發(fā)入門(mén)語(yǔ)言的新手遣鼓,在網(wǎng)絡(luò)編程時(shí)往往容易存在以下兩個(gè)問(wèn)題:
- 沒(méi)有網(wǎng)絡(luò)層,網(wǎng)絡(luò)請(qǐng)求的代碼散落在各處重贺,難以統(tǒng)一管理骑祟,難以維護(hù)。
- 根據(jù)JSON手寫(xiě)Model气笙,采用SwiftyJSON這種半自動(dòng)的方式進(jìn)行JSON解析次企,再手工將經(jīng)SwiftyJSON處理的半成品數(shù)據(jù)灌入Model之中,效率極其低下潜圃,代碼冗余繁雜缸棵。
首先我會(huì)提出一個(gè)網(wǎng)絡(luò)層設(shè)計(jì)方案,之后是一個(gè)基于該設(shè)計(jì)的完整Swift網(wǎng)絡(luò)編程實(shí)踐秉犹。
沒(méi)耐心看漁的可以直接看最后的魚(yú)蛉谜。
網(wǎng)絡(luò)層設(shè)計(jì)
在設(shè)計(jì)之前,我參考了@反革命攻城獅 的 iOS應(yīng)用架構(gòu)談 網(wǎng)絡(luò)層設(shè)計(jì)方案
他設(shè)計(jì)的網(wǎng)絡(luò)層包括兩部分: Manager 和 Reformer崇堵。
每個(gè)業(yè)務(wù)層擁有自己的Manager實(shí)例和Reformer實(shí)例型诚,Manager實(shí)例負(fù)責(zé)發(fā)送請(qǐng)求,取得JSON數(shù)據(jù)鸳劳,JSON經(jīng)Reformer處理后狰贯,采用Delegate的方式通知調(diào)用的業(yè)務(wù)層。
我的設(shè)計(jì)只包含Manager赏廓,而且各個(gè)業(yè)務(wù)層不擁有自己的Manager實(shí)例涵紊,Manager類本身提供一個(gè)單例,單例的實(shí)例方法是對(duì)各個(gè)API的調(diào)用幔摸,每個(gè)方法對(duì)應(yīng)一個(gè)API摸柄,業(yè)務(wù)層將completionHandler傳給Manager單例的相應(yīng)方法,該方法發(fā)出網(wǎng)絡(luò)請(qǐng)求既忆,并對(duì)得到的JSON進(jìn)行解析驱负,將解析完得到的Model傳回業(yè)務(wù)層嗦玖。
設(shè)計(jì)上,我把他講的回調(diào)時(shí)不用block跃脊,不要在Manager內(nèi)解析JSON這兩條都違背了..宇挫,關(guān)于回調(diào)時(shí)用不用block,因?yàn)槲也捎玫氖菃卫沂酰椅也幌雽SON解析下放給業(yè)務(wù)層器瘪,如果硬要采用Delegate,那我就不得不為每個(gè)業(yè)務(wù)層提供一個(gè)專門(mén)的Delegate來(lái)為其進(jìn)行JSON解析绘雁,如果有N個(gè)業(yè)務(wù)層橡疼,我需要先定義N個(gè)Protocol,再為Manager增加N個(gè)變量咧七,同時(shí)還需要使N個(gè)業(yè)務(wù)層接受單例的委托衰齐,這是不現(xiàn)實(shí)的,如果采用block继阻,所需要做的就只是在相應(yīng)方法內(nèi)為該block添加上JSON解析的代碼然后傳給Alamofire就可以了。關(guān)于為什么在Manager內(nèi)解析JSON了废酷,我覺(jué)得這一行代碼的問(wèn)題.. 就不用給業(yè)務(wù)層了吧瘟檩。
最佳實(shí)踐嘗試
OC傳統(tǒng)的JSON解析方式是利用插件或其他工具依照J(rèn)SON生成Model的代碼,然后將得到的JSON利用YYModel或MJExtension之類的第三方庫(kù)一行注入到Model中澈蟆,的確比文章開(kāi)頭提到的方法簡(jiǎn)單多了墨辛,但雖然MJExtension這類的第三方庫(kù)支持Swift,但我在嘗試使用的時(shí)候遇到了NSArray和Array不兼容的問(wèn)題趴俘,可能是我的配置有問(wèn)題睹簇,但我找到了其他可以替代而且更簡(jiǎn)單的方式,就是用JSONExport寥闪。
JSONExport可以根據(jù)JSON為你生成對(duì)應(yīng)的Model代碼太惠,并且也迭代的幫你寫(xiě)好了JSON轉(zhuǎn)Model的代碼,即Model的 formDictionary 方法疲憋。
魚(yú)
第一步:https://github.com/Ahmed-Ali/JSONExport 下一個(gè)JSONExport
第二步:將JSON復(fù)制進(jìn)去
得到三個(gè)Model文件凿渊,拖進(jìn)你的工程,選中copyIfNeeded
第三步:依照之前的網(wǎng)絡(luò)層設(shè)計(jì)缚柳,建一個(gè)Manager埃脏,提供一個(gè)單例,并為每個(gè)API寫(xiě)一個(gè)接受完成閉包的方法秋忙。
class NetworkingManager {
static let sharedInstance = NetworkingManager()
func requestDataForMainPage(completionHandler: (mainPage: FirstPage?) -> Void) {
}
}
第四步:使用Alamofire發(fā)送網(wǎng)絡(luò)請(qǐng)求彩掐,在完成閉包里使用 JSONExport 為每個(gè)Model提供的fromDictionary方法將JSON灌進(jìn)Model中,將Model傳給業(yè)務(wù)層送來(lái)的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)
}
}
第五步:業(yè)務(wù)層的代碼通過(guò)調(diào)用單例的對(duì)應(yīng)函數(shù)訪問(wèn)相應(yīng)API堵幽,并直接獲得該API所對(duì)應(yīng)的Model狗超。
NetworkingManager.sharedInstance.requestDataForMainPage { (mainPage) in
if let exist = mainPage {
self.mainPage = exist
print("something")
} else {
print("nothing")
}
}
這樣既實(shí)現(xiàn)了網(wǎng)絡(luò)訪問(wèn)的統(tǒng)一管理又避免了大段的JSON解析代碼,比起原來(lái)的方法谐檀,不知道高到哪里去了抡谐。