RxSwift + Moya + HandyJSON + 各種插件搭建響應式網絡架構

<font color=red>🧚. RxSwift + Moya + HandyJSON + Plugins.👒👒👒</font>


English | 簡體中文

基於 RxSwift + Moya 搭建響應式數據綁定網絡API架構

MoyaNetwork

該模塊是基於Moya封裝的網絡API架構

  • 主要分爲以下8部分:
    • NetworkConfig:在程序最開始處設置配置信息,全局通用
      • addDebugging:是否開啓默認加入調試插件
      • baseURL:根路徑地址
      • baseParameters:默認基本參數,類似:userID,token等
      • baseMethod:默認請求類型
      • updateBaseParametersWithValue:更新默認基本參數數據
    • RxMoyaProvider:對網絡請求添加響應式,返回Observable序列
    • NetworkUtil:網絡相關函數
      • defaultPlugin:添加默認插件
      • transformAPIObservableJSON:轉換成可觀察序列JSON對象
      • handyConfigurationPlugin:處理配置插件
    • PluginSubType:繼承替換Moya插件協議,方便後序擴展
      • configuration:設置網絡配置信息之後,開始準備請求之前,該方法可以用於本地緩存存在時直接拋出數據而不用再執行後序網絡請求等場景
      • lastNever:最後的最後網絡響應返回時刻,該方法可以用於密鑰失效重新去獲取密鑰然後自動再次網絡請求等場景
    • NetworkAPI:在TargetType基礎上增加協議屬性和封裝基礎網絡請求
      • ip:根路徑地址
      • parameters:請求參數
      • plugins:插件數組
      • stubBehavior:是否走測試數據
      • retry:請求失敗重試次數
      • request:網絡請求方法,返回可觀察序列JSON對象
    • NetworkAPI+ExtNetworkAPI協議默認實現方案
    • NetworkAPIOO:面向對象轉換器,面向協議模式轉面向對象,方便習慣OC思維的小夥伴
      • cdy_ip:根路徑地址
      • cdy_path:請求路徑
      • cdy_parameters:請求參數
      • cdy_plugins:插件
      • cdy_testJSON:測試數據
      • cdy_testTime:測試數據返回時間,默認半秒
      • cdy_retry:請求失敗重試次數
      • cdy_HTTPRequest:網絡請求方法
    • NetworkX:擴展函數方法等
      • toJSON:對象轉JSON字符串
      • toDictionary:JSON字符串轉字典
      • +=:字典拼接

🎷 - 面向對象使用示例1:

class OOViewModel: NSObject {
    let disposeBag = DisposeBag()
    let data = PublishRelay<String>()

    func loadData() {
        var api = NetworkAPIOO.init()
        api.cdy_ip = "https://www.httpbin.org"
        api.cdy_path = "/ip"
        api.cdy_method = .get
        api.cdy_plugins = [NetworkLoadingPlugin.init()]
        api.cdy_retry = 3

        api.cdy_HTTPRequest()
            .asObservable()
            .compactMap{ (($0 as! NSDictionary)["origin"] as? String) }
            .catchAndReturn("")
            .bind(to: data)
            .disposed(by: disposeBag)
    }
}

🎷 - MVP使用示例2:

enum LoadingAPI {
    case test2(String)
}

extension LoadingAPI: NetworkAPI {
    var ip: APIHost {
        return NetworkConfig.baseURL
    }

    var path: String {
        return "/post"
    }

    var parameters: APIParameters? {
        switch self {
        case .test2(let string): return ["key": string]
        }
    }
}

class LoadingViewModel: NSObject {
    let disposeBag = DisposeBag()
    let data = PublishRelay<NSDictionary>()

    /// 配置加載動畫插件
    let APIProvider: MoyaProvider<MultiTarget> = {
        let configuration = URLSessionConfiguration.default
        configuration.headers = .default
        configuration.timeoutIntervalForRequest = 30
        let session = Moya.Session(configuration: configuration, startRequestsImmediately: false)
        let loading = NetworkLoadingPlugin.init()
        return MoyaProvider<MultiTarget>(session: session, plugins: [loading])
    }()

    func loadData() {
        APIProvider.rx.request(api: LoadingAPI.test2("666"))
            .asObservable()
            .subscribe { [weak self] (event) in
                if let dict = event.element as? NSDictionary {
                    self?.data.accept(dict)
                }
            }.disposed(by: disposeBag)
    }
}

🎷 - MVVM使用示例3:

class CacheViewModel: NSObject {
    let disposeBag = DisposeBag()
    struct Input {
        let count: Int
    }
    struct Output {
        let items: Driver<[CacheModel]>
    }

    func transform(input: Input) -> Output {
        let elements = BehaviorRelay<[CacheModel]>(value: [])
        let output = Output(items: elements.asDriver())
        request(input.count)
            .asObservable()
            .bind(to: elements)
            .disposed(by: disposeBag)
            
        return output
    }
}

extension CacheViewModel {
    func request(_ count: Int) -> Driver<[CacheModel]> {
        CacheAPI.cache(count).request()
            .asObservable()
            .mapHandyJSON(HandyDataModel<[CacheModel]>.self)
            .compactMap { $0.data }
            .observe(on: MainScheduler.instance) // 結果在主線程返回
            .delay(.seconds(1), scheduler: MainScheduler.instance) // 延時1秒返回
            .asDriver(onErrorJustReturn: []) // 錯誤時刻返回空
    }
}

🎷 - 鏈式請求使用示例4:

class ChainViewModel: NSObject {
    let disposeBag = DisposeBag()
    let data = PublishRelay<NSDictionary>()

    func chainLoad() {
        requestIP()
            .flatMapLatest(requestData)
            .subscribe(onNext: { [weak self] data in
                self?.data.accept(data)
            }, onError: {
                print("Network Failed: \($0)")
            }).disposed(by: disposeBag)
    }
}

extension ChainViewModel {
    func requestIP() -> Observable<String> {
        return ChainAPI.test.request()
            .asObservable()
            .map { ($0 as! NSDictionary)["origin"] as! String }
            .catchAndReturn("") // 異常拋出
    }

    func requestData(_ ip: String) -> Observable<NSDictionary> {
        return ChainAPI.test2(ip).request()
            .asObservable()
            .map { ($0 as! NSDictionary) }
            .catchAndReturn(["data": "nil"])
    }
}

🎷 - 批量請求使用示例5:

class BatchViewModel: NSObject {
    let disposeBag = DisposeBag()
    let data = PublishRelay<NSDictionary>()

    /// 配置加載動畫插件
    let APIProvider: MoyaProvider<MultiTarget> = {
        let configuration = URLSessionConfiguration.default
        configuration.headers = .default
        configuration.timeoutIntervalForRequest = 30
        let session = Moya.Session(configuration: configuration, startRequestsImmediately: false)
        let loading = NetworkLoadingPlugin.init()
        return MoyaProvider<MultiTarget>(session: session, plugins: [loading])
    }()

    func batchLoad() {
        Observable.zip(
            APIProvider.rx.request(api: BatchAPI.test).asObservable(),
            APIProvider.rx.request(api: BatchAPI.test2("666")).asObservable(),
            APIProvider.rx.request(api: BatchAPI.test3).asObservable()
        ).subscribe(onNext: { [weak self] in
            guard var data1 = $0 as? Dictionary<String, Any>,
                  let data2 = $1 as? Dictionary<String, Any>,
                  let data3 = $2 as? Dictionary<String, Any> else {
                      return
                  }
            data1 += data2
            data1 += data3
            self?.data.accept(data1)
        }, onError: {
            print("Network Failed: \($0)")
        }).disposed(by: disposeBag)
    }
}

MoyaPlugins

該模塊主要就是基於moya封裝網絡相關插件

  • 目前已封裝6款插件供您使用:

🏠 - 簡單使用,在API協議當中實現該協議方法,然後將插件加入其中即可:

var plugins: APIPlugins {
    let cache = NetworkCachePlugin(cacheType: .networkElseCache)
    let loading = NetworkLoadingPlugin.init(delayHideHUD: 0.5)
    loading.changeHudCallback = { (hud) in
        hud.detailsLabel.textColor = UIColor.yellow
    }
    return [loading, cache]
}

HandyJSON

該模塊是基於HandyJSON封裝網絡數據解析

  • 大致分爲以下3個部分:
    • HandyDataModel:網絡外層數據模型
    • HandyJSONError:解析錯誤相關
    • RxHandyJSON:HandyJSON數據解析,目前提供兩種解析方案
      • 方案1 - 結合HandyDataModel模型使用解析出data數據
      • 方案2 - 根據keyPath解析出指定key的數據,前提條件數據源必須字典形式

🎷 - 結合網絡部分使用示例:

func request(_ count: Int) -> Driver<[CacheModel]> {
    CacheAPI.cache(count).request()
        .asObservable()
        .mapHandyJSON(HandyDataModel<[CacheModel]>.self)
        .compactMap { $0.data }
        .observe(on: MainScheduler.instance) // 結果在主線程返回
        .delay(.seconds(1), scheduler: MainScheduler.instance) // 延時1秒返回
        .asDriver(onErrorJustReturn: []) // 錯誤時刻返回空
}

CocoaPods Install

Ex: 導入網絡架構API
- pod 'RxNetworks/MoyaNetwork'

Ex: 導入數據解析
- pod 'RxNetworks/HandyJSON'

Ex: 導入加載動畫插件
- pod 'RxNetworks/MoyaPlugins/Loading'

Demo

大體流程差不多就是這樣,Demo也寫的很詳細,大家可以自己去看看🎷

RxNetworksDemo

提示:如果覺得有幫助,請幫忙點個星 ⭐..

謝謝.🎇

  • 後序有相關插件,也會慢慢補充..

✌️

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章