Swift風(fēng)格的網(wǎng)絡(luò)封裝

本文應(yīng)用到的Swift特性 :枚舉,函數(shù)式編程,可選項(xiàng)

本文是接著上一篇文章寫(xiě)的,網(wǎng)絡(luò)是我們項(xiàng)目中一塊很重要的內(nèi)容,上篇文章中的寫(xiě)法可以滿(mǎn)足我們簡(jiǎn)單的網(wǎng)絡(luò)求情,但是卻不夠Swift。運(yùn)用Swift里的enum、Monad、范型等Swift特性,可以讓我們寫(xiě)出更Swift化的代碼,這篇文章是用Swift2.3寫(xiě)的。
在以前的代碼里,我們的網(wǎng)絡(luò)回調(diào)Handle往往是這樣寫(xiě)的:typealias CompletionHandle = (result:AnyObject?, error:NSError?) -> ()

 func login(account:String,pwd:String,completion: (success: Bool, errorMsg: String?) -> ()) {
        let loginParams= ["account": account, "password": pwd]
        post(APPURLRequest("auth/login", params: loginParams)) { (success, object) -> () in
            dispatch_async(dispatch_get_main_queue(), { () -> Void in
                if success {
                    completion(success: true, errorMsg: nil)
                } else {
                    var errorMsg= "error"
                    if let object = object, let passedMessage = object["message"] as? String {
                        errorMsg= passedMessage
                    }
                    completion(success: true, errorMsg: errorMsg)
                }
            })
        }
    }

在代碼中往往需要判斷是否有error或者是否有result,又或者兩者都有值,這種寫(xiě)法在Swift里不夠nice,看起來(lái)也不美觀。我們的目標(biāo)是這樣的:

func login(params:loginParams,completion:(result:NetworkResult<NSData>) -> ()) {
        NetworkManager.post("path", params: loginParams)
        .response { (result) in
            result
              .successful({ (value) in
                //登錄成功   JSON解析
                //拿到數(shù)據(jù)
                //進(jìn)一步處理
            })
            .failured({ (error, obj) in
                //登錄失敗   UI處理
            })
        }
    }

運(yùn)用enum,范型

Swift里的enum可以有關(guān)聯(lián)值,現(xiàn)在定義這樣一個(gè)enum:

enum APIResult<T>{
    case failure(NSError,AnyObject?)
    case success(T)
    
    func flatMap<U>( transform : (T) throws -> U?) rethrows -> APIResult<U>{
        switch self {
        case let .failure(error,obj):
            return .failure(error,obj)
        case let .success(value):
            guard let newValue = try transform(value) else{
                return .failure(error,obj)
            }
            return .success(newValue)
        }
    }
    
    func map<U>( transform: (T) throws -> U) rethrows -> APIResult<U>{
        return try flatMap {
            try transform($0)
        }
    }
 }   

這個(gè)enum里包含了兩個(gè)case,分別對(duì)應(yīng)我們網(wǎng)絡(luò)返回的result和error,另外,在enum中還寫(xiě)了APIResult的解包方法,方便我們解包。范型用在這里,我們可以應(yīng)對(duì)網(wǎng)絡(luò)請(qǐng)求以外的對(duì)錯(cuò)結(jié)果。接下來(lái)是網(wǎng)絡(luò)的封裝,
在Alamofire中,有兩個(gè)類(lèi),一個(gè)是Request,另一個(gè)是Manager,nice的解耦思路,而且還可以用Swift漂亮的鏈?zhǔn)秸{(diào)用。其中Request負(fù)責(zé)對(duì)請(qǐng)求處理,Manager負(fù)責(zé)方法包裝,線(xiàn)程和回調(diào)的管理。這是我們的Enum就派上用處了,completionHandle里可以用NetworkResult做為回調(diào)參數(shù),Request可以是這樣的:

class NetworkRequest:NSMutableURLRequest{
    ......//可以寫(xiě)多種處理
 func response(completion:(result:NetworkResult<NSData>) -> ()) {
        dataTask(self, completion: completion)
 }
    private func dataTask(request: NSMutableURLRequest, completion:(result:NetworkResult<NSData>) -> ()) {
        let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
        session.dataTaskWithRequest(request) { (data, response, error) -> Void in
            //處理錯(cuò)誤 error
            if let data = data {
                let response = response as? NSHTTPURLResponse
                if response != nil &&  response!.statusCode == 200{
                    completion(result: NetworkResult.success(data))
                } else {
                    //可以自定義錯(cuò)誤 error
                    completion(result: NetworkResult.failed(error!, "error"))
                }
            }else{
                completion(result: NetworkResult.failed(error!, "error"))
            }
            }.resume()
    }

然后再封裝一個(gè)Manager,對(duì)POST、GET、DownLoad、Upload等請(qǐng)求方式進(jìn)行封裝,Manager的可以是這樣的:

class NetworkManager {
  
    static let instance = NetworkManager()
    class var shareInstance:NetworkManager{
        return instance
    }
    private override init() {}  // 阻止init方法
    
 func post(path: String, params: Dictionary<String, AnyObject>? = nil) -> NetworkRequest {
        //返回一個(gè)處理好的POST請(qǐng)求Request實(shí)例
        let request = NetworkRequest(URL: NSURL(string: path)!)
        request.HTTPMethod = NetworkMethodEnum.POST.rawValue
        if let params = params {
            var paramString = ""
            for (key, value) in params {
                let escapedKey = key.stringByAddingPercentEncodingWithAllowedCharacters(.URLQueryAllowedCharacterSet())
                let escapedValue = value.stringByAddingPercentEncodingWithAllowedCharacters(.URLQueryAllowedCharacterSet())
                paramString += "\(escapedKey)=\(escapedValue)&"
            }
            request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
            request.HTTPBody = paramString.dataUsingEncoding(NSUTF8StringEncoding)
        }
        return request
    }

    //還包括GET,PUT,DELETE等。。。
}

接下來(lái),我們就可以華麗麗的調(diào)用Swift Style的網(wǎng)絡(luò)請(qǐng)求了。但是之前還要做一件事情,如果直接調(diào)用的話(huà),我們需要處理NetworkRequest的case,這樣也不夠Swift,我們?cè)侔袾etworkRequest進(jìn)行改造,將其中的解包方法改成如下:

func successful(success: (value: T) -> Void) -> NetworkRequest<T> {
        switch self{
        case let .success(value):
            success(value: value)
        default:
            break
        }
        return self
    }
    
    func failured(failure:(error: NSError,obj: AnyObject?) -> Void) -> NetworkRequest<T> {
        switch self {
        case let .failed(error, obj):
            failed(error, obj)
        default:
            break
        }
        return self
    }

接下來(lái)就可以華麗麗的調(diào)用了,例如下:

func login(params:loginParams,completion:(result:NetworkResult<NSData>) -> ()) {
        NetworkManager.post("path", params: loginParams)
        .response { (result) in
            result
              .successful({ (value) in
                //登錄成功   JSON解析
                //拿到數(shù)據(jù)
                //進(jìn)一步處理
            })
            .failured({ (error, obj) in
                //登錄失敗   UI處理
            })
        }
    }

參考自:
包涵卿在中國(guó)Swift開(kāi)大者大會(huì)上的演講
Alamofire

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,309評(píng)論 4 61
  • 國(guó)家電網(wǎng)公司企業(yè)標(biāo)準(zhǔn)(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報(bào)批稿:20170802 前言: 排版 ...
    庭說(shuō)閱讀 12,406評(píng)論 6 13
  • What is Agile? Agile is a time boxed, iterative approach ...
    Andy010閱讀 1,031評(píng)論 0 2
  • 天氣開(kāi)始轉(zhuǎn)涼了,早晚都這么覺(jué)得。 有一年秋天出門(mén),看見(jiàn)一輛車(chē)駛過(guò),滿(mǎn)地的黃葉就這樣憑空起舞,打著旋飛起,又打著旋落...
    常二先生閱讀 804評(píng)論 0 4
  • 5.8——5.14 整理好了心情,開(kāi)始出發(fā)! 星期一 我種一個(gè)了小盆栽,寓意著新的事物都將重新的開(kāi)始。 去測(cè)我...
    一個(gè)半拍閱讀 324評(píng)論 0 2

友情鏈接更多精彩內(nèi)容