一、安装 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)