Swift Moya

網(wǎng)絡(luò)層這一塊用Alamofire,如同于在oc中用AFNetworking.但是,如果你直接使用的話,會使得各種網(wǎng)絡(luò)請求操作分布很凌亂,所以我選擇了巧神封裝的YTKNetwork,很好用,有興趣的可以看一下.當(dāng)然你也可以自己組織封裝.
這段代碼就是LZ項(xiàng)目中的網(wǎng)絡(luò)請求:

NSDictionary *parameterDic = @{kPageSizeKey:@"10",kCurPageKey:@"1",kLastIDKey:@"0"};
[[WCRequestDataManager sharedRequestDataManager] requestDataForNetWorkWithDataHandleType:WCProductListDataHandleType
    parameterDic:parameterDic
    completed:^(WCProductResultModel *resultModel) {}
    failure:^(NSString *msg) {}
];
  • parameterDic就是請求所需的參數(shù),如果沒有直接傳入nil
  • WCProductListDataHandleType是枚舉類型,你可以理解為它對應(yīng)了產(chǎn)品列表網(wǎng)絡(luò)請求的method(GET/POST),URL等等
  • completedfailure2個(gè)block分別對應(yīng)請求成功失敗兩種情況,并返回頁面需要的model和失敗的信息
  • 數(shù)據(jù)解析直接在對應(yīng)的RequestHandle中,保證返回對應(yīng)的model->WCProductResultModel

那么Swift中推薦一下Moya,這是一個(gè)基于Alamofire的更高層網(wǎng)絡(luò)請求封裝抽象層.
整個(gè)Demo可以在這里下載到:MoyaTest
可以對比一下直接用Alamofire和用Moya請求樣式:

        Alamofire.request(.GET, kRequestServerKey + "services/creditor/product/list/page/2/0/0").responseJSON {
            response in
            if let value = response.result.value {
                let result = Mapper<CommonInfo>().map(value)
                let dataList = Mapper<ProductModel>().mapArray(result?.data?["result"])
                print("Alamofire = \(dataList?[0].productDesc)") // Alamofire = Optional("gfhgfgfhgshgdsfdshgfshfgh")
            }
        }
        
        MoyaTest.sharedInstance.requestDataWithTarget(.productList(pageSize: 2, curpage: 0, lastID: 0), type: ProductModel.self, successClosure: { result in
                let dataList = Mapper<ProductModel>().mapArray(result["result"])
                print("Moya = \(dataList?[0].productDesc)") // Moya = Optional("gfhgfgfhgshgdsfdshgfshfgh")
            }) { errorMsg in
                print(errorMsg)
        }

可見,第二種隱藏了url,method,json解析等參數(shù)/操作,抽象出了一層通用的請求方法.(按理說Mapper<ProductModel>().mapArray(result["result"])不應(yīng)該出現(xiàn)在回調(diào)的閉包中,返回的就應(yīng)該是productList請求對應(yīng)的model,否則type這個(gè)參數(shù)就沒有意義了,這個(gè)梗會在下面說到)


看一下文檔說明:

<font size="5" color="IndianRed">Targets</font>

使用Moya的第一步就是定義一個(gè)Target:通常是指一些符合TargetType protocolenum.然,你請求的其余部分都只根據(jù)這個(gè)Target而來.這個(gè)枚舉用來定義你的網(wǎng)絡(luò)請求API的行為action.

public enum RequestApi {
   //  UserApi
   case login(loginName: String, password: String)
   case register //(userMobile: String, password: String, inviteCode: String, verifyCode: String)
   //case accountInfo

   //  ProductApi
   case productList(pageSize: Int, curpage: Int, lastID: Int)
//    case productDetail(id: Int)
}
  • 強(qiáng)烈推薦Swift 中枚舉高級用法及實(shí)踐這篇文章,涵蓋了枚舉幾乎所有的知識點(diǎn).enum在Swift中的作用,簡直不要太牛!
  • 再推薦一個(gè)用模式匹配解析 URL,通過關(guān)聯(lián)值(Associated Value)來定義請求所需的參數(shù)(loginName和password也可以省略掉,但為了直觀的說明,還是保留一下)
extension RequestApi: TargetType {
   public var baseURL: NSURL {
       return NSURL(string: "http://apptest.wecube.com:8080/taojinjia/")!
   }
   
   public var path: String {
       switch self {
           case .login(_,_):
               return "services/crane/sso/login/doLogin"
           case .register:
               return "services/crane/sso/login/register"
           case let .productList(pageSize, curpage, lastID):
               return "services/creditor/product/list/page/"+String(pageSize)+"/"+String(curpage)+"/"+String(lastID)
       }
   }
   
   public var method: Moya.Method {
       switch self {
           case .login(_,_), .register:
               return .POST
           case .productList(_,_,_):
               return .GET
       }
   }
   
   public var parameters: [String: AnyObject]? {
       switch self {
           case let .login(loginName, password):
               return ["loginName": loginName, "userPassword": password]
           default :
               return nil
       }
   }
   
   //  單元測試用
   public var sampleData: NSData {
       return "{}".dataUsingEncoding(NSUTF8StringEncoding)!
   }
}

定義的enum實(shí)現(xiàn)TargetType協(xié)議,完成一系列初始化設(shè)置:

  • <font color="IndianRed">baseURL</font>:統(tǒng)一設(shè)置服務(wù)器地址,測試切換非常的方便,YTKNetwork中也是這樣配置的.
  • <font color="IndianRed">path</font>:每個(gè)請求需求對應(yīng)的各自的請求路徑
   參見源碼,最終的url就是由baseURL和path拼接而來
   public final class func DefaultEndpointMapping(target: Target) -> Endpoint<Target> {
       let url = target.baseURL.URLByAppendingPathComponent(target.path).absoluteString
       return Endpoint(URL: url, sampleResponseClosure: {.NetworkResponse(200, target.sampleData)}, method: target.method, parameters: target.parameters)
   }
  • <font color="IndianRed">method</font>:不解釋...請求方式
  • <font color="IndianRed">parameters</font>:需要的參數(shù)
  • <font color="IndianRed">sampleData</font>:方便于單元測試...暫時(shí)忽略

<font size="5" color="IndianRed">ProvidersEndpoints</font>

providerendpoints是緊密相關(guān)的,放在一起講更好點(diǎn)(名字都怪怪的,果然國外開發(fā)者取名都是講究哇)

let requestProvider = RxMoyaProvider<RequestApi>()

最終的請求發(fā)起對象就是requestProvider,RxMoyaProviderMoyaProvider的子類,你需要在podfile中導(dǎo)入Moya/RxSwift,當(dāng)然你也可以直接用MoyaProvider來完成初始化,RxSwift目前只是簡單的了解了一下,具體用法這里暫時(shí)忽略,不影響請求的完成.
你可能發(fā)現(xiàn),這跟endpoints并沒什么關(guān)系,但是,看下源碼:

    /// Initializes a provider.
    public init(endpointClosure: EndpointClosure = MoyaProvider.DefaultEndpointMapping,
        requestClosure: RequestClosure = MoyaProvider.DefaultRequestMapping,
        stubClosure: StubClosure = MoyaProvider.NeverStub,
        manager: Manager = Alamofire.Manager.sharedInstance,
        plugins: [PluginType] = []) {
            
            self.endpointClosure = endpointClosure
            self.requestClosure = requestClosure
            self.stubClosure = stubClosure
            self.manager = manager
            self.plugins = plugins
    }

    /// Mark: Defaults

public extension MoyaProvider {
    
    // These functions are default mappings to endpoings and requests.
    
    public final class func DefaultEndpointMapping(target: Target) -> Endpoint<Target> {
        let url = target.baseURL.URLByAppendingPathComponent(target.path).absoluteString
        return Endpoint(URL: url, sampleResponseClosure: {.NetworkResponse(200, target.sampleData)}, method: target.method, parameters: target.parameters)
    }
    
    public final class func DefaultRequestMapping(endpoint: Endpoint<Target>, closure: NSURLRequest -> Void) {
        return closure(endpoint.urlRequest)
    }
}
  • init的4個(gè)參數(shù)都給了默認(rèn)參數(shù),且默認(rèn)的endpointDefaultEndpointMapping如同它的名字一樣,"終結(jié)點(diǎn)"匹配了網(wǎng)絡(luò)請求要的因素.
  • 如果你的請求需要添加請求頭,你也能夠通過endpointByAddingHTTPHeaderFields方法來實(shí)現(xiàn).
  • Target貫穿了全局,在endpoint的配置中,也可以通過刷選不同的枚舉值來設(shè)置不同情況.
  • 還有一些高級用法就自己去研究文檔,LZ的英文實(shí)在是渣的可怕...
    在上面的栗子中,選擇了默認(rèn)的初始化方法.

<font size="5" color="IndianRed">Request</font>

import Foundation
import Moya
import RxSwift
import ObjectMapper
import SwiftyJSON

typealias SuccessClosure = (result: AnyObject) -> Void
//typealias SuccessClosure = (result: Mappable) -> Void
typealias FailClosure = (errorMsg: String?) -> Void

enum RequestCode: String {
    case failError = "0"
    case success = "1"
}

class MoyaTest {
    static let sharedInstance = MoyaTest()
    private init(){}
    
    let requestProvider = RxMoyaProvider<RequestApi>()
    
    func requestDataWithTarget<T: Mappable>(target: RequestApi, type: T.Type , successClosure: SuccessClosure, failClosure: FailClosure) {
        let _ = requestProvider.request(target).subscribe { (event) -> Void in
            switch event {
            case .Next(let response):
                let info = Mapper<CommonInfo>().map(JSON(data: response.data,options: .AllowFragments).object)
                guard info?.code == RequestCode.success.rawValue else {
                    failClosure(errorMsg: info?.msg)
                    return
                }
                guard let data = info?.data else {
                    failClosure(errorMsg: "數(shù)據(jù)為空")
                    return
                }
                successClosure(result: data)
            case .Error(let error):
                print("網(wǎng)絡(luò)請求失敗...\(error)")
            default:
                break
            }
        }
    }
}

最后的請求方法封裝,如上面的栗子:

  • json的解析我用的SwiftyJsonObjectMapper.SwiftyJson主要是用來把data轉(zhuǎn)為object(這里如果調(diào)用JSON(response.data)會無法解析,要顯式的加上參數(shù)options,但其實(shí)JSON(xxx)內(nèi)部是默認(rèn)實(shí)現(xiàn)了的,實(shí)在不明白為什么會解析失敗...參數(shù)的解釋參見hit mehit me too),后面的轉(zhuǎn)model用的就是ObjectMapper.<font color="red">這里補(bǔ)上前面提到的:為什么沒能夠做到返回直接是請求數(shù)據(jù)對應(yīng)的model,而多做了一步let dataList = Mapper<ProductModel>().mapArray(result["result"])</font>
// 服務(wù)器給的數(shù)據(jù)格式統(tǒng)一為
{
   "code" = "",
   "data" =  {} 或 ({}),
   "msg" = ""
}

data對應(yīng)的就是請求url返回的model[model],那么就是不是調(diào)用successClosure(result: data)了,而是

               //typealias SuccessClosure = (result: Mappable) -> Void
                let model = Mapper<T>().map(data)
               successClosure(result: model)

有的接口data對應(yīng)的是包含了多個(gè)dic的數(shù)組,感覺解決方法就是再單獨(dú)開一個(gè)數(shù)組的請求方法,調(diào)用mapArray,這里就不多加描述了,反正都一樣的流程.
productList的url返回的data里面還包了一層resultpageVO,so...這就是一個(gè)特殊情況_!

  • RxSwift...學(xué)習(xí)中

ok!差不多Moya的基本使用就是這樣啦,感覺還是非常方便實(shí)用的.

參考資料
通過 Moya+RxSwift+Argo 完成網(wǎng)絡(luò)請求
RxSwift

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

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,886評論 18 139
  • Moya + RxSwift Moya + RxSwift 最簡單的使用方法是這樣的: Object Mapper...
    jkyeo閱讀 9,240評論 9 34
  • 本文主要是練習(xí)Moya的熟練使用 簡單的網(wǎng)絡(luò)請求 1.創(chuàng)建baseTargetType 主要是添加了baseUrl...
    JokAr_閱讀 8,556評論 1 9
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,767評論 0 9
  • 在oc中目前大部分網(wǎng)絡(luò)請求來自于AFNetworking ,但是在項(xiàng)目中我們并不會直接去使用它,一般會在次進(jìn)行包裝...
    sttech閱讀 370評論 0 1