RxSift网络请求处理

前段时间,公司项目重构,决定采用RxSwift,这是一个函数式响应编程的框架,我差不多也是提前学习了一点,然后就边学习边开始了,期间也是遇到了各种问题,还好项目也算是按时交付测试了。这篇文章主要是用来讲述RxSwift网络请求的用法。

RxSwift+Moya+ObjectMapper 网络请求与处理

Moya简单的介绍

Moya是一个基于Alamofire的Networking library,并且添加了对于ReactiveCocoa和RxSwift的接口支持。

Moya使用

1.首先需要通过枚举对请求进行明确分类

public enum XYJHomeRouter {
    /// 刷新首页
    case refreshHome(parameters: [String:Any])
    /// 获取bannar
    case getBannar(parameters: [String:Any])
}

2.让XYJHomeRouter枚举遵守TargetType协议,这个Target便是你网络请求相关行为的定义,也可以自定义协议,我们实现这些协议,也就相当于完成了网络请求需要的endpoint.
自定义的协议

public protocol XYJTargetType {
    var isShow: Bool { get }
}

XYJHomeRouter的实现

extension XYJHomeRouter: TargetType,XYJTargetType {
    
    public var baseURL: URL {
        return URL(string: baseHostString)!
    }
    
    public var path: String {
        switch self {
        case .refreshHome:
            return "yd/app/home"
        case .getBannar:
            return "yd/app/common/banners"
        }
    }
    public var method: Moya.Method {
        switch self {
        case .refreshHome:
            return .post
        case .getBannar:
            return .post
        }
    }
    public var parameters: [String: Any]? {
        switch self {
        case .refreshHome(parameters: let dict):
            return dict
        case .getBannar(parameters: let dict):
            return dict
        }
    }
    public var parameterEncoding: ParameterEncoding {
        return JSONEncoding.default
    }
    public var task: Task {
        return .request
    }
    
    public var validate: Bool {
        return false
    }
    //自己定义的协议实现,是否显示正在加载,有的接口在后台请求,不需要告诉用户
    public var isShow: Bool {
        switch self {
        case .refreshHome:
            return false
        case .getBannar:
            return false
        }
    }

3.在viewmodel中进行网络请求方法的封装

  // 获取banner数据
    func getBanner(paramaters: [String: Any]) -> Observable> {
        return XYJMoyaHttp().sendRequest().request(.getBannar(parameters: paramaters)).mapObject(XYJResultList.self)
    }

我们看下XYJMoyaHttp()的实现
参数:
EndpointClosure:可以对请求参数做进一步的修改,如可以修改endpointByAddingParameters endpointByAddingHTTPHeaderFields等
RequestClosure:你可以在发送请求前,做点手脚.判断有无网络做气泡提示 ,修改超时时间,打印一些数据等
StubClosure:可以设置请求的延迟时间,可以当做模拟慢速网络
Manager:请求网络请求的方式。默认是Alamofire
[PluginType] :Moya提供了一个插件机制,使我们可以建立自己的插件类来做一些额外的事情。比如写Log,显示“菊花”等。抽离出Plugin层的目的,就是把和自己网络无关的行为抽离。避免各种业务揉在一起不利于扩展

public class XYJMoyaHttp {
    func sendRequest() -> RxMoyaProvider {
        return RxMoyaProvider.init(endpointClosure: endpointClosure ,requestClosure: requestClosure,stubClosure: stubClosure,plugins: [NetworkLoggerPlugin.init(verbose: true,responseDataFormatter: {JSONResponseDataFormatter($0)}),spinerPlugin,XYJMoyaResponseNetPlugin()])
    }

    func sendUploadMultipart() -> RxMoyaProvider {
        return RxMoyaProvider.init(endpointClosure: endpointClosure ,requestClosure: requestClosure ,plugins: [NetworkLoggerPlugin.init(verbose: true,responseDataFormatter: {JSONResponseDataFormatter($0)}),spinerPlugin,XYJMoyaResponseNetPlugin()])
    }
    // MARK: - 设置请求头部信息
    let endpointClosure = { (target: T) -> Endpoint in
        let url = target.baseURL.appendingPathComponent(target.path).absoluteString
        let endpoint = Endpoint(
            url: url,
            sampleResponseClosure: { .networkResponse(200, target.sampleData) },
            method: target.method,
            parameters: target.parameters,
            parameterEncoding: target.parameterEncoding
        )
        //在这里设置你的HTTP头部信息
        return endpoint.adding(newHTTPHeaderFields: [

            :])
    }


    // 发请求之前判断是否有网络
    let requestClosure = { (endpoint: Endpoint, done: MoyaProvider.RequestResultClosure) in
        if var request = endpoint.urlRequest {
            if(XYJNetworkMonitor.shareInstance.hasNetwork()) {
                done(Result.success(request))
            } else {
                done(Result.failure(MoyaError.requestMapping(noNetWorkTipsString)))
            }
        }
    }
    
    /// 单元测试代码
    let stubClosure: (_ type: T) -> Moya.StubBehavior  = { type1 in
        return StubBehavior.never
    }
    
}

/// 日志
///
/// - Parameter data: data数据
/// - Returns: Data数据类型
private func JSONResponseDataFormatter(_ data: Data) -> Data {
    do {

        let dataAsJSON = try JSONSerialization.jsonObject(with: data)
        let prettyData =  try JSONSerialization.data(withJSONObject: dataAsJSON, options: .prettyPrinted)
        return prettyData
    } catch {
        return data // fallback to original data if it can't be serialized.
    }
}

/// 指示灯的配置的初始化
let spinerPlugin = XYJNetworkActivityPlugin { state in
    guard let currentView = XYJLogVC.instance.currentVC?.view else {
        return
    }
    if state == .began {
        XYJProgressHUD.hide(view: currentView)//失把指示灯关掉,再显示
        XYJProgressHUD.showAdded(view: currentView)
    } else {
        XYJProgressHUD.hide(view: currentView)
    }
}

class XYJLogVC {
    var currentVC: UIViewController?
    //声明一个单例对象
    static let instance = XYJLogVC()
    private init() {}
}

public protocol XYJTargetType {

    var isShow: Bool { get }
}
网络请求reposne的处理插件

可以根据返回响应的状态码判断业务成功或者失败,还可以再这里进行某个特殊状态码的全局逻辑业务处理,比如某个状态码,要进行弹出登录处理等

/// reposne的处理(net)
public final class XYJMoyaResponseNetPlugin: PluginType {
    /// 成功的状态码
    let normalCode =  ["0","0000"]
    //// 修改response的值
    public func process(_ result: Result, target: TargetType) -> Result {
        var result = result
        //JSONSerialization
        if case .success(let response) = result {
            let processedResponse = Response(statusCode: -1, data: response.data, request: response.request, response: response.response)
            guard let json = try? JSONSerialization.jsonObject(with: response.data, options: .allowFragments) as? [String:Any] , let code = json?["code"] as? String else {
                   return .failure(.jsonMapping(processedResponse))
            }
        if( !normalCode.contains(code) ) {  //业务失败
                if code == "C0001" {
                  //清理缓存,弹出登录框的逻辑
                }
                result = .failure(.statusCode(processedResponse)) 
            } else {   //业务成功
                let data = json?["data"]
                if let jsonDatas = data as? Data {
                    guard  let jsonData = try? JSONSerialization.data(withJSONObject: jsonDatas, options: []) else {
                        return .failure(.jsonMapping(processedResponse))
                    }
                    result = .success(Response(statusCode: -1, data: jsonData, request: response.request, response: response.response))
                } else {
                    result = .success(Response(statusCode: -1, data: response.data, request: response.request, response: response.response))
                }
            }
        }
        return result
    }

4.在控制器中进行调用

  vm.getBanner(paramaters: paras as! [String : Any]).asObservable().subscribe(onNext: { (result) in
            guard let bannars = result.data else {
                return
            }
            self.vm.cacheBanner(datas: bannars)
            self.bannerView.datas = bannars
        }, onError: { (_) in
            XYJLog(message: "获取bannar数据失败")
        }).disposed(by: self.disposeBag)

JSON解析

我们可以单独新建一个文件,用来对Moya的Response和ObservableType进行扩展

extension Response {
    // 这一个主要是将JSON解析为单个的Model
    public func mapObject(_ type: T.Type) throws -> T {
        guard let json = try? JSONSerialization.jsonObject(with: self.data, options: .allowFragments) as? [String:Any] else {
            throw MoyaError.jsonMapping(self)
        }
        guard let object = Mapper().map(JSONObject:json) else {
            throw MoyaError.jsonMapping(self)
        }
        return object
    }

    // 这个主要是将JSON解析成多个Model并返回一个数组,不同的json格式写法不相同
    public func mapArray(_ type: T.Type) throws -> [T] {

        guard let json = try? JSONSerialization.jsonObject(with: self.data, options: .allowFragments) as? [String:Any] else {
            throw MoyaError.jsonMapping(self)
        }

        guard let jsonDic =  json?["data"] as? [[String: Any]] else {
            throw MoyaError.jsonMapping(self)
        }

        guard let objects = Mapper().mapArray(JSONArray: jsonDic) else {
                throw MoyaError.jsonMapping(self)
        }
        return objects
    }
}

extension ObservableType where E == Response {
    // 这个是将JSON解析为Observable类型的Model
    public func mapObject(_ type: T.Type) -> Observable {
        return flatMap { response -> Observable in
            return Observable.just(try response.mapObject(T.self))
        }
    }

    // 这个是将JSON解析为Observable类型的[Model]
    public func mapArray(_ type: T.Type) -> Observable<[T]> {
        return flatMap { response -> Observable<[T]> in
            return Observable.just(try response.mapArray(T.self))
        }
    }

ObjectMapper

ObjectMapper是用Swift语言实现对象和JSON相互转换的框架,自定义的model需要实现Mappable协议,ObjectMapper可以很好的处理泛型类型的数据,不过这个泛型需要实现Mappable协议,也可以处理好嵌套的数据结构
下面我们看下具体的使用
原始数据

{
    "code": "0",
    "data": {
        "expected_credit": "0",
        "noread_msg": "4",
        "role_type": "1",
        "total_credit": "0",
        "nick_name": "哦哦哦",
        "wait_bill": "1",
        "total_bill": "6"
    },
    "message": "成功"
}

数据模型处理

/// 返回结果模型
//T为泛型,遵循Mappable协议
class XYJResult: Mappable {
    var code: String?
    var message: String?
    var data: T?
    
    required init?(map: Map) {
    }
    init() {
    }
    
    func mapping(map: Map) {
        code <- map["code"]
        message <- map["message"]
        data <- map["data"]
    }
}


import UIKit
import ObjectMapper
class XYJHomeModel: Mappable {
    var nickName: String?
    var noReadMsg: String?
    var expectedCred: String?
    var totalBill: String?
    var totalCredit: String?
    var waitBill: String?
    var monthBill: String?
    //必须要实现的方法
    required init?(map: Map) {
    }
    //手动创建model时要写
    init() {}
   //建立映射关系--
    func mapping(map: Map) {
        nickName <- map["nick_name"]
        noReadMsg <- map["noread_msg"]
        expectedCred <- map["expected_credit"]
        totalBill <- map["total_bill"]
        totalCredit <- map["total_credit"]
        monthBill <- map["month_bill"]
        waitBill <- map["wait_bill"]
    }
}

参考资料:
https://github.com/Hearst-DD/ObjectMapper
http://www.codertian.com/2017/02/04/iOS-Moya-RxSwift-better-networking/

你可能感兴趣的:(RxSift网络请求处理)