美文网首页iOS开发实用技术
Moya + ObjectMapper + RxSwift

Moya + ObjectMapper + RxSwift

作者: yunil | 来源:发表于2017-07-27 16:45 被阅读1151次

    使用此三剑客,访问网络更加优雅。

    Moya

    Moya基于Alamofire的更高层网络请求封装抽象层.使用Moya访问网络有以下优点:
    1.编译时检查正确的API端点访问.
    2.使你定义不同端点枚举值对应相应的用途更加明晰.
    3.提高测试地位从而使单元测试更加容易.

    ObjectMapper

    ObjectMapper是一个基于Swift语言开发的能够让 JSON 与 Object 之间轻易转换的类库。通过ObjectMapper我们可以将 JSON 数据转换成 Model 对象或将 Model 对象转换成 JSON 数据。

    RxSwift

    RxSwiftSwift函数响应式编程的一个开源库,由Github的ReactiveX组织开发,维护。
    RxSwift的目的是让让数据/事件流和异步任务能够更方便的序列化处理,能够使用swift进行响应式编程。
    在这里我强烈建议学习下RxSwift,尤其在使用MVVM模式下,可以实现双向绑定,下面推荐一些学习RxSwift的学习资源。
    小菁的视频

    CocoaPods

    使用Moya + ObjectMapper + RxSwift最好使用CocoaPods进行集成,可以方便的管理项目里面的第三方库。关于如何使用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,实现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关键字的,mutating 只针对值类型,如枚举,结构体 )

    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,我这里是自己定制。主要是处理关于ssl的问题,具体可以点击关于对manger如何添加受信任的白名单。EndpointClosure可以对请求参数做进一步的修改,如可以修改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参数:\(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请求头,比如一些网络访问的token.
    同时可以在此插件中添加网络访问指示器,willSend中启动指示器,didReceive中关闭指示器,process方法主要是用于对访问网络的结果进行修改再返回。另外关于MoyaError的错误我们该如何解析,需要使用到switch来先解析MoyaError,然后再解析underlying,将解析出来的Error转换成NSError,最后输入错误结果。

    NetworkLoggerPlugin可以用来进行日志的管理,NetworkActivityPlugin可以在此插件中处理状态栏中的小菊花.

    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

    访问网络前我们需要先实例化一个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我们则使用mapObjectArray则使用mapArray
    参考文章:
    http://www.jianshu.com/p/a728d03db236
    最后希望大家对此文章做出指点,好改正不足。谢谢

    相关文章

      网友评论

        本文标题:Moya + ObjectMapper + RxSwift

        本文链接:https://www.haomeiwen.com/subject/tpwzkxtx.html