Swift中优雅的网络请求:Moya+RxSwift

Moya是一个对Alamofire封装的库,提供简洁的接口供开发者调用,抽象了URL和Parameters来帮助使用者生成urlRequest,最后通过alamofire发起请求。
具体使用时在Moya和Your App之间加一层Rx,用于处理请求回来的数据

先来看看Moya的具体实现和使用方式

Moya使用的是面向协议(POP)编程的思想

//可以从发起网络请求看起,碰到对应的协议或类再回来看具体细节

Moya中的协议包括:
1.TargetType协议,用来生成request的必要参数
baseURL
pathmethod
sampleData:做测试模拟的数据
task:生成具体的task,可以传入请求的参数
headers:
Moya中的类包括:

Class1 Endpoint:用来灵活配置网络请求
property list:
  • url
  • method
  • task
  • sampleData
  • SampleResponseClosure
  • HttpHeaderFields
    和TargetType协议的元素意义对应,在框架中通过TargetType初始化Endpoint类
method list:
  • methodF: urlRequest() -> URLRequest
  • adding(newHTTPHeaderFields:[String:String]) -> Endpoint 添加新的Header返回新的Endpoint    
  • replacing(task:Task)-> Endpoint 替换task返回新的Endpoint
Class2 MoyaProvider<Target: TargetType>: MoyaProviderType
method list :
  • methodA: defaultEndpointMapping(for target:Target) -> Endpoint ,通过TargetType类型生成Endpoint

  • methodB:defaultRequestMapping(for target:Endpoint, closure: RequestResultClosure) ,调用 methodF 通过Endpoint 中methodF生成URLRequest,根据生成情况执行RequestResultClosure

  • methodC:performRequest(很多参数)根据endpoint中不同的task调用不同的发起请求的方法,其中包含methodD

  • methodD:sendRequest()传入URLRequest,调用Alamofire方法生成Alamofire发起请求的DataRequest类型,传入并调用methodE

  • methodE:sendAlamofireRequest() 传入DataRequest,用Alamofire发起请求

发起网络请求的过程:
1.初始化各类
  • 1.创建枚举类型,实现TargetType协议的方法。使用枚举的原因:使用枚举结合switch管理api更加方便。具体可以查看《Swift的枚举》
  • 2.用TargetType、EndpointClosure、requestClosure 初始化MoyaProvider类,初始化时可以提供endpointClosure,类型为 (Target) -> Endpoint,不传使用defaultEndpointMapping
  • 3.可以自定义requestClosure,来自定义URL生成时候的错误情况处理。比如参数错误,加密错误等
2.生成URLreqeust
  • 1.用EndpointClosure传入 TargetType创建Endpoint,
  • 2.创建closure1:performNetworking(RequestResutlClosure类型),closure1内部执行methodC
  • 3.methodB:requestClosure:传入步骤1生成的endpoint ,步骤2生成的performNetworking,执行methodF生成URLRequest
3.发起请求
  • methodB 执行clousre1
  • clousre1 执行methodC
  • methodD
  • methodE
//  methodB
  public final class func defaultRequestMapping(for endpoint: Endpoint, closure: RequestResultClosure) {
        do {
            let urlRequest = try endpoint.urlRequest()
            closure(.success(urlRequest))
        } catch MoyaError.requestMapping(let url) {
            closure(.failure(MoyaError.requestMapping(url)))
        } catch MoyaError.parameterEncoding(let error) {
            closure(.failure(MoyaError.parameterEncoding(error)))
        } catch {
            closure(.failure(MoyaError.underlying(error, nil)))
        }
    }

Moya框架中的POP:

  • protocol MoyaProviderType
    我们可以实现MoyaProviderType中的request方法,来自定义发起请求的方式
  • protocol TargetType
    定义了发起请求需要的参数
Moya和RxSwift:

通过下面的代码把Moya和Rx进行结合,解决callback hell的问题

  • 通过相同的方法可以给类添加rx支持,具体查看另一篇RxSwift的文章
   public func request(_ token: Base.Target, callbackQueue: DispatchQueue? = nil) -> Single<Response> {
        return Single.create { [weak base] single in
            let cancellableToken = base?.request(token, callbackQueue: callbackQueue, progress: nil) { result in
                switch result {
                case let .success(response):
                    single(.success(response))
                case let .failure(error):
                    single(.error(error))
                }
            }

            return Disposables.create {
                cancellableToken?.cancel()
            }
        }
    }
  • callback hell如下图
Moya和数据模型:

response有解析Decodable的模型数据默认实现,可以直接使用,也可以自定义map的实现,来根据项目需求自定义解析过程(比如使用MJ),根据不同状况抛出error。

下面为自定义的代码:

extension Response {
    func mapModel<T: Codable>(_ type: T.Type) throws -> T {
        print(String.init(data: data, encoding: .utf8) ?? "")
        
        guard let json = try mapJSON() as? [String: Any] else {
            throw MoyaError.jsonMapping(self)
        }
         //这里可以添加不同情况的报错
                  //        if(error) {
        //            throw MoyaError.jsonMapping(self)
        //        }
        return (type as! NSObject.Type).mj_object(withKeyValues: json["data"]) as! T
    }
}

public extension PrimitiveSequence where TraitType == SingleTrait, ElementType == Response {
    func MJObjectMap<T>(_ type: T.Type,_ handleErr:Bool = true) -> Single<T> {
        return flatMap { response in
            return Single.just(try response.mapModel(T.self, handleErr))
        }
    }
}

最后在项目中的网络请求时这样发起的:

let bag = DisposeBag()
let provider = MoyaProvider<GitHub.GetUserProfile>()

provider.rx.request(GitHub.GetUserProfile(name: "yoxisem544"))
  .filterSuccessfulStatusCodes()
  .map(Profile.self)
  .subscribe(onSuccess: { p in
    print(p)
  }, onError: { e in
    print(e.localizedDescription)
  })
  .disposed(by: bag) 
posted @ 2020-08-21 20:13  书院柯浩然  阅读(4252)  评论(0编辑  收藏  举报