淺談Swift網絡編程最佳實踐

序言

以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復制進去

JSONExport

得到三個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解析代碼,比起原來的方法,不知道高到哪里去了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容