Swift 輕量級網絡層設計

前言

廣泛咱們的網絡層設計的時候直接是以下結構APIManager.post(url, parameter,completeHandle),服務器配置在APIManager.m文件中進行配置。這樣一個簡單便捷網絡請求類便寫好了,但細心思考咱們會發現以下一些問題:git

相同API可能分散各處致使每次須要填寫的參數key值。回調處理代碼也可能會存在冗餘。

//例如登陸功能  APIManager.post("登陸APIUrl",{"name":"","pwd":""},completeHandle)
//登陸功能首先必然存在於LoginVc中
LoginVC ==> 登陸功能 
//此時要求實現自動登陸功能,那麼在主控制器生成時應該判斷是否能夠自動登陸(固然也能夠推出LoginVc後再去自動登陸)
MainVc ==> 自動登陸(登陸功能)

此時便須要在兩處來寫入url、parameter及回調方法,當此接口有更新時就須要在兩處進行修改,若工程中用到的此網絡功能越多則須要修改的地方也越多!github

部分網絡請求須要隨着頁面的pop而取消,手動進行管理task顯得笨拙而麻煩。

關於網絡請求起飛後回調沒有着陸點是很危險的一件事,所以正常的作法是讓APIManager.post返回task任務,並有當前請求發起類持有,並在當前請求類析構而且task任務未完成時取消。這樣的操做方式一看就繁瑣,若一個大工程到處這樣維護怕是至關麻煩了!json

所以咱們便須要設計出一個能解決上述問題的網絡層設計swift

設計模式

設計示例圖

此種設計主要是請求類持有自定義Request,並向中間件BQAPIManager傳入Request調用URLSession。此過程當中Request會持有task任務,當請求類被釋放時,Request也會釋放,此時判斷其是否有任務執行若有任務執行則中止執行。從而達到自動取消任務的功能。而此時針對相同的url及其參數即可封裝與一個Request中,可有效的減小冗餘代碼!設計模式

關鍵類說明

BQRequest.Swift

主要在於封裝參數key值和設定url並處理網絡請求類容及持有並按需取消或釋放task任務api

//關鍵代碼以下
    var method: HTTPMethod = .post
    var result: Any?//如成功處理網絡請求內容後result有值
    weak var task: URLSessionTask?

    public func url() -> String { return "" }//API接口
    //模型轉字典
    public func toDiction() -> [String: Any] {
        let mir = Mirror(reflecting: self)
        var dict = [String: AnyObject]()
        for p in mir.children {
            if p.label == "method" || p.label == "result" || p.label == "task"{
                continue
            }
            dict[p.label!] = (p.value as AnyObject)
        }
        return dict
    }
    //請求內容處理
    public func responseAction(data: Data?, response: URLResponse?, error: Error?) {
        if let data = data {
            do {
                self.result = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
            } catch let err as NSError {
                print(err.localizedDescription)
            }
        }
        if let error = error {
            print(error.localizedDescription)
        }
    }
    //保證析構時(請求類不存在時)取消正在執行的任務
    deinit {
        if let task = self.task{
            if task.state == .running || task.state == .suspended {
                print("cancel \(task)")
                task.cancel()
            }
            self.task = nil
        }
    }

BQAPIManager.Swift

主要做用爲配置服務器環境(方便本地、測試服、正式服的切換),並經過傳入的Request發起網絡請求。
其中主要的關鍵點在與避免Request與task的循環持有。服務器

//關鍵代碼以下
public class func sendRequest(request: BQRequest, completionHandler:@escaping () -> ()) {
        weak var req = request
        let task = BQNetWork.sendRequest(urlstr: baseUrl + request.url(), parameter: request.toDiction(), method: request.method, time: 10, headers: nil) { (data, response, error) in
            //對返回內容進行處理,若是處理成功結果賦予Request.result之中
            req?.responseAction(data: data, response: response, error: error)
            DispatchQueue.main.async {
                if req?.task != nil {
                    completionHandler()
                    //任務完成後釋放task任務
                    req?.result = nil//此處最好讓網絡調用類來決定是否置空result
                    req?.task = nil
                }
            }
        }
        request.task = task
    }

其中BQNetWork中的內容是基於URLSession的一個簡單封裝,這裏便再也不詳述網絡

使用方式

以上述登陸功能爲示例,此時需設計LoginRequest繼承與BQRequest並重寫其url 及 responseAction方法async

public name: String?
    public pwd: String?
    override public func url() -> String { return "登陸APIUrl" }
    override public func responseAction(data: Data?, response: URLResponse?, error: Error?) {
        super.responseAction(data: data, response: response, error:error);
        if let result = self.result {
            //登陸處理通用功能實現
        } 
    }

接着讓LoginVc持有LoginRequest對name及pwd賦值(減小因參數key字段出錯而失敗的狀況),並經過BQAPIManager發起請求便可。ide

後記

這種設計方式主要是經過對Moya的模仿變化而來,其作到了最簡單、方便的管理。固然在實際運用中可能也會出現問題,如發現問題或有何不妥之處望指正!謝謝!最後附上筆者本身的Swift工具庫

相關文章
相關標籤/搜索