Moya + ObjectMapper + RxSwift

使用此三劍客,訪問網(wǎng)絡(luò)更加優(yōu)雅荞彼。

Moya

Moya基于Alamofire的更高層網(wǎng)絡(luò)請求封裝抽象層.使用Moya訪問網(wǎng)絡(luò)有以下優(yōu)點:
1.編譯時檢查正確的API端點訪問.
2.使你定義不同端點枚舉值對應相應的用途更加明晰.
3.提高測試地位從而使單元測試更加容易.

ObjectMapper

ObjectMapper是一個基于Swift語言開發(fā)的能夠讓 JSON 與 Object 之間輕易轉(zhuǎn)換的類庫帜乞。通過ObjectMapper我們可以將 JSON 數(shù)據(jù)轉(zhuǎn)換成 Model 對象或?qū)?Model 對象轉(zhuǎn)換成 JSON 數(shù)據(jù)库说。

RxSwift

RxSwiftSwift函數(shù)響應式編程的一個開源庫,由Github的ReactiveX組織開發(fā)割疾,維護馋艺。
RxSwift的目的是讓讓數(shù)據(jù)/事件流和異步任務能夠更方便的序列化處理栅干,能夠使用swift進行響應式編程。
在這里我強烈建議學習下RxSwift丈钙,尤其在使用MVVM模式下非驮,可以實現(xiàn)雙向綁定,下面推薦一些學習RxSwift的學習資源雏赦。
小菁的視頻

CocoaPods

使用Moya + ObjectMapper + RxSwift最好使用CocoaPods進行集成,可以方便的管理項目里面的第三方庫芙扎。關(guān)于如何使用CocoaPods星岗,請google。
使用CocoaPods將下列第三方庫進行集成戒洼。

platform :ios,'8.0'
use_frameworks!
target'YzzSwift'do
inherit! :search_paths
inhibit_all_warnings!
pod'RxSwift'
pod'RxCocoa'
pod'Moya'
pod'Moya/RxSwift'
pod'ObjectMapper'

ObjectMapper

首先建立一個AccountModel.swift俏橘,導入ObjectMapper,實現(xiàn)Mappable

import ObjectMapper

struct AccountModel: Mappable {


    var accountID:String!
    var avatarIcon:String?
    var nickName:String?
    var tk:String!
    var userType:String!
    
    init?(map: Map) {
        
    }
    
    
    mutating func mapping(map: Map) {
        accountID <- map["accountID"]
        avatarIcon <- map["avatarIcon"]
        nickName <- map["nickName"]
        tk <- map["tk"]
        userType <- map["userType"]
    }
    

    
}

在類中是無需標注mutating關(guān)鍵字的圈浇,mutating 只針對值類型寥掐,如枚舉靴寂,結(jié)構(gòu)體 )

RequestManger

新建RequestManger.swift

import Alamofire
import Moya
class RequestManger {

    class CustomServerTrustPoliceManager : ServerTrustPolicyManager {
        override func serverTrustPolicy(forHost host: String) -> ServerTrustPolicy? {
            return .disableEvaluation
        }
        public init() {
            super.init(policies: [:])
        }
    }
    
    
    class func defaultAlamofireManager() -> Manager {
        let configuration = URLSessionConfiguration.default
        configuration.httpAdditionalHeaders = Alamofire.SessionManager.defaultHTTPHeaders
        let manager = Alamofire.SessionManager(configuration: configuration,serverTrustPolicyManager: CustomServerTrustPoliceManager())
        
        manager.startRequestsImmediately = false
        return manager
    }
    
    
    class func endpointMapping<Target: TargetType>(target: Target) -> Endpoint<Target> {
        
        return MoyaProvider.defaultEndpointMapping(for: target)
    }
   
}

注意:默認是Alamfire,我這里是自己定制召耘。主要是處理關(guān)于ssl的問題百炬,具體可以點擊關(guān)于對manger如何添加受信任的白名單。EndpointClosure可以對請求參數(shù)做進一步的修改,如可以修改endpointByAddingParameters endpointByAddingHTTPHeaderFields等

RequestPlugin

Moya中默認提供的3個插件

import Moya
import Result
import SVProgressHUD
class RequestPlugin: PluginType {
    
    let tk = "jKle07aY0pbSaP8ACk8_ZktK-0ivmFUTIDoLnDBw_FOSCw=="
    let accountID = "1000024"
    let tz = "Asia/Shanghai"

    func prepare(_ request: URLRequest, target: TargetType) -> URLRequest {
        var request = request
        request.addValue(tk, forHTTPHeaderField: "tk")
        request.addValue(accountID, forHTTPHeaderField: "accountID")
        request.addValue(tz, forHTTPHeaderField: "tz")
        return request
    }
    
    func willSend(_ request: RequestType, target: TargetType) {
           print("*****************************************************************************\n請求連接:\(target.baseURL)\(target.path) \n方法:\(target.method)\n參數(shù):\(String(describing: target.parameters)) ")

        SVProgressHUD.show()
    }
    
    func didReceive(_ result: Result<Response, MoyaError>, target: TargetType) {
    
        SVProgressHUD.dismiss()
        let result = result
        switch result {
        case .failure(let moyaError):
            switch moyaError {
            case .underlying(let error) :
                let errorNs = error as NSError
                print(errorNs.code)
                break
            default:
                break
            }
        
            break
        default:
            break
        }
    }
    func process(_ result: Result<Response, MoyaError>, target: TargetType) -> Result<Response,     MoyaError> {
        return result
    }

}

注意:這里可以在prepare中添加request請求頭污它,比如一些網(wǎng)絡(luò)訪問的token.
同時可以在此插件中添加網(wǎng)絡(luò)訪問指示器剖踊,willSend中啟動指示器,didReceive中關(guān)閉指示器衫贬,process方法主要是用于對訪問網(wǎng)絡(luò)的結(jié)果進行修改再返回德澈。另外關(guān)于MoyaError的錯誤我們該如何解析,需要使用到switch來先解析MoyaError固惯,然后再解析underlying梆造,將解析出來的Error轉(zhuǎn)換成NSError,最后輸入錯誤結(jié)果。

NetworkLoggerPlugin可以用來進行日志的管理葬毫,NetworkActivityPlugin可以在此插件中處理狀態(tài)欄中的小菊花.

TargetType

新建VesyncApi.swift

import Moya
import Alamofire



enum VesyncApi{
    case login(account: String, password: String)
    case deviceList()
}

extension VesyncApi:TargetType {
    
    var baseURL: URL {
        return URL(string: "https://192.168.100.60:5005")!
    }
    
    var path: String {
        switch self {
        case .login(_, _):
            return "/vold/user/login"
        case .deviceList():
            return "/vold/user/devices"
            
        }
    }
    
    var method: Moya.Method  {
        switch self {
        case .login(_, _):
            return .post
            
        case .deviceList:
            return .get
        }
    }
    
    var parameters: [String : Any]? {
        switch self {
        case let .login(account, password):
            return ["account": account, "password": password]
        default :
            return nil
        }
    }
    
    public var parameterEncoding: ParameterEncoding {
        return JSONEncoding.default
    }
    
    
    var sampleData: Data {
//        switch self {
//        case .login(_, _):
            return "".data(using: String.Encoding.utf8)!
            
//        }
    }
    
    var task: Task {
//        switch self {
//        case .login(_, _):
            return .request
//        }
    }
    
}

RxSwift和ObjectMapper的中間件

新建Observable+ObjectMapper.swift

import Foundation
import RxSwift
import Moya
import ObjectMapper
import Result
extension Observable {
    func mapObject<T: Mappable>(type: T.Type) -> Observable<T> {
        return self.map { response in
            //if response is a dictionary, then use ObjectMapper to map the dictionary
            //if not throw an error
            guard let dict = response as? [String: Any] else {
                throw RxSwiftMoyaError.ParseJSONError
            }
            
            if let error = self.parseError(response: dict) {
                throw error
            }
   
             
            return Mapper<T>().map(JSON: dict)!
        }
    }
    
    func mapArray<T: Mappable>(type: T.Type) -> Observable<[T]> {
        return self.map { response in
            guard let dict = response as? [[String: Any]] else {
                throw RxSwiftMoyaError.ParseJSONError
            }
            
            if let error = self.parseError(response: dict) {
                throw error
            }
            return Mapper<T>().mapArray(JSONArray: dicts)
        }
    }
    
    func parseServerError() -> Observable {
        return self.map { (response) in
            let name = type(of: response)
            print(name)
            guard let dict = response as? [String: Any] else {
                throw RxSwiftMoyaError.ParseJSONError
            }
            if let error = self.parseError(response: dict) {
                throw error
            }
            return self as! Element
        }   
    }
    
    fileprivate func parseError(response: [String: Any]?) -> NSError? {
        var error: NSError?
        if let value = response {
            var code:Int?
            var msg:String?
            if let errorDic = value["error"] as? [String:Any]{
                code = errorDic["code"] as? Int
                msg = errorDic["msg"] as? String
                error = NSError(domain: "Network", code: code!, userInfo: [NSLocalizedDescriptionKey: msg ?? ""])
            }
        }
        return error
    }
}

enum RxSwiftMoyaError: String {
    case ParseJSONError
    case OtherError
}
extension RxSwiftMoyaError: Swift.Error {}

provider

訪問網(wǎng)絡(luò)前我們需要先實例化一個provider

 let provider = RxMoyaProvider<VesyncApi>(endpointClosure: RequestManger.endpointMapping,manager:RequestManger.defaultAlamofireManager(),plugins:[RequestPlugin()])

RxSwiftObjectMapper配合使用訪問

        provider.request(.login(account: account, password: password))
           .mapJSON().mapObject(type: AccountModel.self).subscribe(onNext: { (model) in
                print(model.accountID)
            }, onError: { (error) in
               
                let e = error as NSError
                print(e.code)
                
            }).addDisposableTo(disposeBag)

注意:如果是object我們則使用mapObject镇辉,Array則使用mapArray
參考文章:
http://www.reibang.com/p/a728d03db236
最后希望大家對此文章做出指點,好改正不足供常。謝謝

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末摊聋,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子栈暇,更是在濱河造成了極大的恐慌麻裁,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件源祈,死亡現(xiàn)場離奇詭異煎源,居然都是意外死亡,警方通過查閱死者的電腦和手機香缺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門手销,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人图张,你說我怎么就攤上這事锋拖。” “怎么了祸轮?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵兽埃,是天一觀的道長。 經(jīng)常有香客問我适袜,道長柄错,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮售貌,結(jié)果婚禮上给猾,老公的妹妹穿的比我還像新娘。我一直安慰自己颂跨,他們只是感情好敢伸,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著毫捣,像睡著了一般详拙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蔓同,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天饶辙,我揣著相機與錄音,去河邊找鬼斑粱。 笑死弃揽,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的则北。 我是一名探鬼主播矿微,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼尚揣!你這毒婦竟也來了涌矢?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤快骗,失蹤者是張志新(化名)和其女友劉穎娜庇,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體方篮,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡名秀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了藕溅。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片匕得。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖巾表,靈堂內(nèi)的尸體忽然破棺而出汁掠,到底是詐尸還是另有隱情,我是刑警寧澤集币,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站惠猿,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏偶妖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一趾访、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧扼鞋,春花似錦申鱼、人聲如沸云头。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至昏滴,卻和暖如春猴鲫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背谣殊。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工拂共, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人姻几。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓宜狐,卻偏偏與公主長得像,于是被迫代替她去往敵國和親鲜棠。 傳聞我的和親對象是個殘疾皇子肌厨,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

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

  • 開始戒煙
    jumbojing閱讀 550評論 0 0
  • 在家里過了很多個夏天,唯獨這一個讓我多了份焦灼豁陆「贪郑可能是覺得這樣的夏天越過越少,每一個明天都被追趕著盒音,卻也沒...
    稻香8724閱讀 224評論 0 0
  • iOS加速度傳感器 加速度傳感器是根據(jù)x表鳍,y和z三個方向來檢測設(shè)備位置的變化 iOS設(shè)備正面朝上放置,加速度傳感器...
    Maggie的小蝸居閱讀 2,186評論 0 0