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) 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM