网络请求框架Moya使用一

一、安装 Moya(CocoaPods)

pod "Moya/RxSwift”

pod “HandyJSON”

二、基本用法

1、实现  TargetType  协议

2、实现  PluginType 插件协议

3、扩展 ObservableType 解析 JSON 数据

//MoyaProvider

import Foundation

import Moya

//Provider

let requestProvider = MoyaProvider<RequestManagerProvider>(plugins: [RequestLoadingPlugin()])

//Hud Provider

let requestHudProvider = MoyaProvider<RequestManagerProvider>(plugins: [RequestLoadingPlugin(true)])

enum RequestManagerProvider {

    //密码登录

    case LoginPassword(mobile:String, password:String)

    //注册

    case Register(mobile:String, password: String, code: String)

    //忘记密码

    case ForgetPassword(mobile:String, code: String, password: String, repassword: String)

}

extension RequestManagerProvider: TargetType {


    var baseURL: URL {

        return URL(string: "http://baseurl.com")!

    }


    //请求路径

    var path: String {

        switch self {

        case .LoginPassword:

            return "/login"

        case .Register:

            return "/register"

        case .ForgetPassword:

            return "/forgetpwd"

        }

    }

    //请求方式

    var method: Moya.Method {

        return .post

    }


    //用于单元测试

    var sampleData: Data {

        return "".data(using: String.Encoding.utf8)!

    }


    //请求接口时对应的请求参数

    var task: Task {


        switch self {

        case .LoginPassword(let mobile,let password):


            let bundleId = Bundle.main.bundleIdentifier!


            let formData1 = MultipartFormData(provider: .data(mobile.data(using: .utf8)!), name: "mobile")

            let formData2 = MultipartFormData(provider: .data(password.data(using: .utf8)!), name: "password")

            let formData3 = MultipartFormData(provider: .data(bundleId.data(using: .utf8)!), name: "bundle_id")

            return .uploadMultipart([formData1, formData2,formData3])


        case .Register(let mobile, let password, let code):


            let bundleId = Bundle.main.bundleIdentifier!


            let formData1 = MultipartFormData(provider: .data(mobile.data(using: .utf8)!), name: "mobile")

            let formData2 = MultipartFormData(provider: .data(password.data(using: .utf8)!), name: "password")

            let formData3 = MultipartFormData(provider: .data(code.data(using: .utf8)!), name: "code")

            let formData4 = MultipartFormData(provider: .data(bundleId.data(using: .utf8)!), name: "bundle_id")


            return .uploadMultipart([formData1, formData2, formData3, formData4])

        case .ForgetPassword(let mobile, let code, let password, let repassword):


            let formData1 = MultipartFormData(provider: .data(mobile.data(using: .utf8)!), name: "mobile")

            let formData2 = MultipartFormData(provider: .data(password.data(using: .utf8)!), name: "newpassword")

            let formData3 = MultipartFormData(provider: .data(code.data(using: .utf8)!), name: "code")

            let formData4 = MultipartFormData(provider: .data(repassword.data(using: .utf8)!), name: "repassword")

            return .uploadMultipart([formData1, formData2, formData3, formData4])

        case .VerificationCode(let mobile, let event):

            let formData1 = MultipartFormData(provider: .data(mobile.data(using: .utf8)!), name: "mobile")

            let formData2 = MultipartFormData(provider: .data(event.data(using: .utf8)!), name: "event")

            return .uploadMultipart([formData1, formData2])

        case .RouteList:


            let typeid = Bundle.main.bundleIdentifier! == "com.shanhushanhu.shanhushanhu" ? "1" : "2"


            let formData1 = MultipartFormData(provider: .data(typeid.data(using: .utf8)!), name: "typeid")

            return .uploadMultipart([formData1])

        default:

            return .requestPlain

        }

    }

    //header信息

    var headers: [String : String]? {

        return ["Content-Type":"multipart/form-data"]

    }

}

//请求插件

import Foundation

import Moya

import iProgressHUD

class RequestLoadingPlugin: PluginType {


    var SwiftIsShowHud:Bool = false


    init(_ isShowHud: Bool = false) {

        SwiftIsShowHud = isShowHud

    }



    func prepare(_ request: URLRequest, target: TargetType) -> URLRequest {

        print("prepare")

        var mRequest = request

        mRequest.timeoutInterval = 20

        return mRequest

    }

    func willSend(_ request: RequestType, target: TargetType) {

        print("开始请求")

        if SwiftIsShowHud == true {


            DispatchQueue.main.async {

                let keyViewController = UIApplication.shared.keyWindow?.rootViewController

                if (keyViewController != nil) {


                    iProgressHUD.sharedInstance().attachProgress(toView: (keyViewController!.view)!)

                    keyViewController!.view.showProgress()

                }

            }

        }


    }


    func didReceive(_ result: Result<Response, MoyaError>, target: TargetType) {

        print("结束请求")


        if SwiftIsShowHud == true {

            DispatchQueue.main.async {

                let keyViewController = UIApplication.shared.keyWindow?.rootViewController

                if (keyViewController != nil) {

                    keyViewController!.view.dismissProgress()

                }

            }

        }


//        switch result {

//        case .failure(let error):

//

//            let errorReason: String = error.errorDescription ?? ""

//            var tip = ""

//            if errorReason.contains("The Internet connection appears to be offline") {

//                tip = "网络不给力,请检查您的网络"

//            }else if errorReason.contains("Could not connect to the server") {

//                tip = "无法连接服务器"

//            }else {

//                tip = "请求失败"

//            }

//

//        default:

//            break

//        }

    }

}

//JSON 解析

import Foundation

import RxSwift

import HandyJSON

import Moya

enum DCUError : Swift.Error {

    case ParseJSONError

    case RequestFailed

    case NoResponse

    case UnexpectedResult(resultCode: Int?,resultMsg:String?)

}

enum RequestStatus: Int {

    case requestSuccess = 1

    case requestTokenError = 401

    case requestError

}

fileprivate let RESULT_CODE = "code"

fileprivate let RESULT_MSG = "msg"

fileprivate let RESULT_DATA = "data"

public extension ObservableType {

    func mapResponseToObject<T: HandyJSON>(type: T.Type) -> Observable<T> {

        return map { response in

            guard let response = response as? Moya.Response

                else {

                    throw DCUError.NoResponse

            }

//            guard ((200...209) ~= response.statusCode) else {

//                throw DCUError.RequestFailed

//            }

            ////////////////////////////////////////////////////////////

//            let jsonData = try response.mapJSON() as! [String : Any]

//            if let code = jsonData[RESULT_CODE] as? String {

//                if code == "查询成功" {

//                    if let model = JSONDeserializer<T>.deserializeFrom(dict: jsonData){

//                        return model

//                    }

//                }

//            }

            ////////////////////////////////////////////////////////////

            guard let json = try?JSONSerialization.jsonObject(with: response.data, options: .mutableContainers) as? [String:Any] else {

                throw DCUError.NoResponse

            }            


            if let code = json[RESULT_CODE] as? Int {

                if code == RequestStatus.requestSuccess.rawValue {

                    let data = json[RESULT_DATA]


                    let objects = JSONDeserializer<T>.deserializeFrom(dict: data as? Dictionary)


                    if objects != nil {

                        return objects!

                    }


                    if let data = data as? Data {

                        let jsonString = String(data: data,encoding: .utf8)

                        let object = JSONDeserializer<T>.deserializeFrom(json: jsonString)



                        if object != nil {

                            return object!

                        }else {

                            return T()

//                            throw DCUError.ParseJSONError

                        }

                    }else {

                        return T()

//                        throw DCUError.ParseJSONError

                    }

                }else if code == RequestStatus.requestTokenError.rawValue {

                    // Tocken失效 跳转登录

                    VPNUserManager.sharedInstance().removeUserInfo()

                    NotificationCenter.default.post(name: NotificationName_Login, object: nil)


                    throw DCUError.UnexpectedResult(resultCode:json[RESULT_CODE] as? Int, resultMsg: nil)

                }else {

                    throw DCUError.UnexpectedResult(resultCode:json[RESULT_CODE] as? Int, resultMsg: json[RESULT_MSG] as? String)

                }

            }else {

                throw DCUError.ParseJSONError

            }


        }

    }


    func mapResponseToObjectArray<T: HandyJSON>(type: T.Type) -> Observable<[T]> {

        return map { response in


            // 得到response

            guard let response = response as? Moya.Response else {

                throw DCUError.NoResponse

            }


            // 检查状态码

//            guard ((200...209) ~= response.statusCode) else {

//                throw DCUError.RequestFailed

//            }


            guard let json = try? JSONSerialization.jsonObject(with: response.data, options: JSONSerialization.ReadingOptions(rawValue: 0)) as? [String: Any]  else {

                throw DCUError.NoResponse

            }


            // 服务器返回code

            if let code = json[RESULT_CODE] as? Int {

                if code == RequestStatus.requestSuccess.rawValue {

                    guard let objectsArrays = json[RESULT_DATA] as? NSArray else {

                        throw DCUError.ParseJSONError

                    }

                    // 使用HandyJSON解析成对象数组

                    if let objArray = JSONDeserializer<T>.deserializeModelArrayFrom(array: objectsArrays) {

                        if let objectArray: [T] = objArray as? [T] {

                            return objectArray

                        }else {

                            return [T]()

                        }

                    }else {

                        return [T]()

                    }


                }else if code == RequestStatus.requestTokenError.rawValue {

                    // Tocken失效 跳转登录

                    VPNUserManager.sharedInstance().removeUserInfo()

                    NotificationCenter.default.post(name: NotificationName_Login, object: nil)

                    return [T]()

                } else {

                    throw DCUError.UnexpectedResult(resultCode: json[RESULT_CODE] as? Int , resultMsg: json[RESULT_MSG] as? String)


                }

            } else {

                throw DCUError.ParseJSONError

            }

        }

    }

}

//调用

requestProvider.rx.request(.LoginPassword(mobile: mobile, password: password)).asObservable()

                .mapResponseToObject(type: VPNLoginResultMode.self)

                .subscribe(onNext: { (model) in


                     self.view.dismissProgress()


                    if let token = model.userinfo?.token {

                        VPNUserManager.sharedInstance().saveUserInfo(mobile: mobile,tocken: token)


                        GYHUD.flash(.success, title: "登录成功", onView: self.view, delay: 1) {


                            self.dismiss(animated: true) {}

                        }

                    }

                },onError: { (error) in

                    self.view.dismissProgress()


                    switch error {

                    case DCUError.UnexpectedResult(_,let resultMsg):

                        if let msg = resultMsg { GYHUD.flash(.label, title: msg, onView: self.view, delay: 1.5) }

                    default:

                        GYHUD.flash(.label, title: "请求网络失败!", onView: self.view, delay: 1.5)

                    }


                }).disposed(by: disposeBag)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335