Network API framework

yangkj

Condy

Posted on February 3, 2022

Network API framework

RxNetworks

🧚. RxSwift + Moya + HandyJSON + Plugins.👒👒👒

English | 简体中文

This is a set of infrastructure based on RxSwift + Moya

MoyaNetwork

This module is based on the Moya encapsulated network API architecture.

  • Mainly divided into 8 parts:
    • NetworkConfig: Set the configuration information at the beginning of the program.
      • addDebugging:Whether to introduce the debug mode plugin by default.
      • baseURL: Root path address to base URL.
      • baseParameters: Default basic parameters, like: userID, token, etc.
      • baseMethod: Default request method type.
      • updateBaseParametersWithValue: Update default base parameter value.
    • RxMoyaProvider: Add responsiveness to network requests, returning Single sequence.
    • NetworkUtil: Network related functions
      • defaultPlugin: Add default plugin.
      • transformAPIObservableJSON: Transforms a Observable sequence JSON object.
      • handyConfigurationPlugin: Handles configuration plugins.
    • PluginSubType: Inherit and replace the Moya plug-in protocol to facilitate subsequent expansion.
      • configuration: After setting the network configuration information, this method can be used in scenarios such as throwing data directly when the local cache exists without executing subsequent network requests.
      • lastNever: When the last network response is returned, this method can be used in scenarios such as key failure to re-obtain the key and then automatically re-request the network.
    • NetworkAPI: Add protocol attributes and encapsulate basic network requests based on TargetType.
      • ip: Root path address to base URL.
      • parameters: Request parameters.
      • plugins: Set network plugins.
      • stubBehavior: Whether to take the test data.
      • retry:Network request failure retries.
      • request: Network request method and return a Single sequence object.
    • NetworkAPI+Ext: Protocol default implementation scheme.
    • NetworkAPIOO: OOP converter, MVP to OOP, convenient for friends who are used to OC thinking
      • cdy_ip: Root path address to base URL.
      • cdy_path: Request path.
      • cdy_parameters: Request parameters.
      • cdy_plugins: Set network plugins.
      • cdy_testJSON: Network testing json string.
      • cdy_testTime: Network test data return time, the default is half a second.
      • cdy_HTTPRequest: Network request method and return a Single sequence object.
    • NetworkX: extension function methods etc.
      • toJSON: to JSON string.
      • toDictionary: JSON string to dictionary.
      • +=: Dictionary concatenation.

🎷 - OO Example 1:

class OOViewModel: NSObject {

    struct Input {
        let retry: Int
    }

    struct Output {
        let items: Observable<String>
    }

    func transform(input: Input) -> Output {
        return Output(items: input.request())
    }
}

extension OOViewModel.Input {
    func request() -> Observable<String> {
        var api = NetworkAPIOO.init()
        api.cdy_ip = NetworkConfig.baseURL
        api.cdy_path = "/ip"
        api.cdy_method = APIMethod.get
        api.cdy_plugins = [NetworkLoadingPlugin()]
        api.cdy_retry = self.retry

        return api.cdy_HTTPRequest()
            .asObservable()
            .compactMap{ (($0 as! NSDictionary)["origin"] as? String) }
            .catchAndReturn("")
            .observe(on: MainScheduler.instance)
    }
}
Enter fullscreen mode Exit fullscreen mode

🎷 - MVP Example 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>()

    /// Configure the loading animation plugin
    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)
    }
}
Enter fullscreen mode Exit fullscreen mode

🎷 - MVVM Example 3:

class CacheViewModel: NSObject {

    struct Input {
        let count: Int
    }

    struct Output {
        let items: Observable<[CacheModel]>
    }

    func transform(input: Input) -> Output {
        let items = request(input.count).asObservable()

        return Output(items: items)
    }
}

extension CacheViewModel {

    func request(_ count: Int) -> Observable<[CacheModel]> {
        CacheAPI.cache(count).request()
            .mapHandyJSON(HandyDataModel<[CacheModel]>.self)
            .compactMap { $0.data }
            .observe(on: MainScheduler.instance) // the result is returned on the main thread
            .catchAndReturn([]) // return null on error
    }
}
Enter fullscreen mode Exit fullscreen mode

🎷 - Chain Example 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("") // Exception thrown
    }

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

🎷 - Batch Example 5:

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

    /// Configure loading animation plugin
    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)
    }
}
Enter fullscreen mode Exit fullscreen mode

MoyaPlugins

This module is mainly based on moya package network related plugins

  • At present, 6 plugins have been packaged for you to use:
    • Cache: Network Data Cache Plugin
    • Loading: Load animation plugin
    • Indicator: Indicator plugin
    • Warning: Network failure prompt plugin
    • Debugging: Network printing, built in plugin
    • GZip: Network data unzip plugin

🏠 - Simple to use, implement the protocol method in the API protocol, and then add the plugin to it:

var plugins: APIPlugins {
    let cache = NetworkCachePlugin(cacheType: .networkElseCache)
    let loading = NetworkLoadingPlugin.init(delay: 0.5)
    let warning = NetworkWarningPlugin.init()
    warning.changeHud = { (hud) in
        hud.detailsLabel.textColor = UIColor.yellow
    }
    return [loading, cache, warning]
}
Enter fullscreen mode Exit fullscreen mode

HandyJSON

This module is based on HandyJSON package network data parsing

  • Roughly divided into the following 3 parts:
    • HandyDataModel: Network outer data model
    • HandyJSONError: Parse error related
    • RxHandyJSON: HandyJSON data parsing, currently provides two parsing solutions
      • Option 1: Combine HandyDataModel model to parse out data.
      • Option 2: Parse the data of the specified key according to keyPath, the precondition is that the json data source must be in the form of a dictionary.

🎷 - Example of use in conjunction with the network part:

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)
        .asDriver(onErrorJustReturn: [])
}
Enter fullscreen mode Exit fullscreen mode

CocoaPods Install

Ex: Import Network Architecture API
- pod 'RxNetworks/MoyaNetwork'

Ex: Import Model Anslysis 
- pod 'RxNetworks/HandyJSON'

Ex: Import loading animation plugin
- pod 'RxNetworks/MoyaPlugins/Loading'
Enter fullscreen mode Exit fullscreen mode

Remarks

The general process is almost like this, the Demo is also written in great detail, you can check it out for yourself.🎷

RxNetworksDemo

Tip: If you find it helpful, please help me with a star. If you have any questions or needs, you can also issue.

Thanks.🎇

About the author


💖 💪 🙅 🚩
yangkj
Condy

Posted on February 3, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related