Swift Moya實踐與提升

Moya是什么就不再介紹了,因為網(wǎng)上已經(jīng)有很多關于Moya的基礎使用方法。
Swift:4.1
Moya: 11.0.2
RxSwift: 4.1.2
demo地址:https://github.com/hellolad/RxMoyaDemo

1. Moya TargetType: headers

在以前的Moya版本中,我們要給Reqeust提供Header的時候,TargetType里沒有提供給我們直接使用的,所以我們需要使用Endpoint來給Request加上Header

let publicParamEndpointClosure = { (target: AccountService) -> Endpoint<AccountService> in
    let url = target.baseURL.appendingPathComponent(target.path).absoluteString
    let endpoint = Endpoint<AccountService>(url: url, sampleResponseClosure: { .networkResponse(200, target.sampleData) }, method: target.method, parameters: target.parameters, parameterEncoding: target.parameterEncoding)
    return endpoint.adding(newHTTPHeaderFields: ["x-platform" : "iOS", "x-interface-version" : "1.0"])
}

這是以前的老版本中,需要提供Header的時候就需要這樣寫,非常的復雜和不方便?,F(xiàn)在TargetType提供了headers,所以我們可以擺脫這種寫法了。

2. RxMoya

Moya支持RxSwift,改變以前的方式也非常的簡單,只要在provider后面跟上.rx基本上就可以了.

provider.rx.request(.ip)
    .map(HTTBin.self)
    .subscribe { result in
        switch result {
        case .success(let element):
            guard let bin = element else { return }
            print("useage RxSwift: ", bin.ip)
        case .error(let error):
            print(error.localizedDescription)
        }
    }.disposed(by: bag)

3. Endpoint

在我們的MoyaProvider中提供了三種在初始化Provider的時候的回調(diào)參數(shù):

  1. EndpointClosure
  2. RequestResultClosure
  3. RequestClosure
    這三種文章上面給Request提供Headers的時候已經(jīng)用到了,第二種在項目里,暫時沒有用到。下面介紹一下第三種的用法:
    Moya的請求大致可以看做是:Request->Endpoint->Response的過程,中間的Endpoint是給我們開發(fā)者留下的最后的可以修改Reqeust的地方,如果你錯過了,你就修改不了,下面我們簡單的介紹一下,如果在RequestClousre里提供給Request增加超時的操作:
let requestClosure = { (endpoint: Endpoint, closure: @escaping MoyaProvider<HttpbinService>.RequestResultClosure) in
    do {
        var request = try endpoint.urlRequest()
        request.timeoutInterval = 30
        closure(.success(request))
    } catch {
        print("error:", error)
    }
}
let provider = MoyaProvider<HttpbinService>(requestClosure: requestClosure)

然后調(diào)用的時候初始化Provider就可以了,這種方式真的是...我感覺寫起來很不方便,正好,Moya給我們提供了另一種好東西:Plugin.

4. Plugin

Moya的插件機制非常的好,好在哪里?

  1. 捕獲Request的準備
  2. 捕獲Request的發(fā)送
  3. 捕獲Response
  4. 捕獲整個從請求到收到結果的過程

如何使用在網(wǎng)上也有很多,我們現(xiàn)在把增加超時的地方從Endpoint的回調(diào)里刪掉,放在Plugin里

final class Plugin: PluginType {
    func prepare(_ request: URLRequest, target: TargetType) -> URLRequest {
        print("prepare")
        var mRequest = request
        mRequest.timeoutInterval = 10
        return mRequest
    }
}
let provider = MoyaProvider<HttpbinService>(plugins: [Plugin()])

感覺一下子清爽很多,可能我還不太夠Swifty
插件機制是一個數(shù)組,所以你可以放入多種插件。還有三個函數(shù)我就不介紹了。

5. Moya擴展配合 Rx 和 SwiftyJSON 以及 ObjectMapper寫出優(yōu)雅的網(wǎng)絡請求

通常我們使用Moya來獲取網(wǎng)絡請求是這樣的:

let provider = MoyaProvider<HttpbinService>()
provider.request(.ip) { (result) in
    switch result {
    case .success(let response):
        do {
            let any = try JSONSerialization.jsonObject(with: response.data, options: .allowFragments)
            guard let obj = any as? [String: Any] else {
                return
            }
            /// 解析數(shù)據(jù)
        } catch {
            print(error)
        }
    case .failure(let error):
        print(error)
    }
}

這種操作在很多項目里真的好多好多,這樣寫的缺點是代碼耦合性太強,維護起來很難,沒有進行封裝。有些時候我們可以到Github上找一些大佬封裝好的,我們自己看一下源碼學習一下,然后實踐一下,你會發(fā)現(xiàn)我們的代碼不管是質(zhì)量還是可讀性都會變強。
兩個庫:SwiftyJSON(4.1.0), ObjectMapper(3.1.0)寫這篇文章的時候,我用的版本。

  • 使用RxMoya提供的函數(shù)進行過濾操作
provider.rx.request(.ip)
    .mapJSON()
    .subscribe { result in
        switch result {
        case .success(let element):
            print("element :", element)
        case .error(let error):
            print(error.localizedDescription)
        }
    }.disposed(by: bag)

    }
}
打?。?element : {
    origin = "101.81.57.239";
}

我們使用了RxMoya為我們提供的mapJSON我們很容易就轉換成了JSON并且打印出來,非常方便,那我們有沒有更快的轉換方式,把這個JSON直接轉換成Model發(fā)送出來,下面我們介紹一下ObjectMapper。

  • 使用ObjectMapper
    我現(xiàn)在進行的api都是從http://httpbin.org上進行的測試.
    ObjectMapper相信大家也都使用過了,看看我們的HTTBin的這個結構體:
struct HTTBin: Mappable {
    var ip: String = ""
    
    init?(map: Map) {}
    mutating func mapping(map: Map) {
        ip <- map["origin"]
    }
}
  • 封裝map轉換成Model
    現(xiàn)在我們有了SwiftyJSON也有了ObjectMapper兩個神器,然后我們就可以去偷學怎么使用了,我們看一下mapJSON的實現(xiàn)
public func mapJSON(failsOnEmptyData: Bool = true) -> Single<Any> {
    return flatMap { response -> Single<Any> in
        return Single.just(try response.mapJSON(failsOnEmptyData: failsOnEmptyData))
    }
}

我只想說三個字:666
通過Single的方式發(fā)送出來(還有Observable的方式暫不討論),發(fā)的是Any類型的數(shù)據(jù),那我們就可以發(fā)出來Mappable的數(shù)據(jù),動手:

extension PrimitiveSequence where TraitType == SingleTrait, ElementType == Response {
    func map<T: BaseMappable>(_ type: T.Type) -> Single<T?> {
        return flatMap { response -> Single<T?> in
            do {
                let any = try response.mapJSON()
                guard let dic = any as? [String: Any] else {
                    return Single<T?>.just(nil)
                }
                let mapper = T(JSON: dic)
                return Single<T?>.just(mapper)
                
            } catch {
                return Single<T?>.just(nil)
            }
        }
    }
}

我們通過擴展PrimitiveSequence ,然后定義函數(shù)map<T: BaseMappable>(_ type:)來實現(xiàn)發(fā)送Mappable的函數(shù),具體實現(xiàn)其實就是拿到response轉換成mapJSON,然后轉換成字典,最后通過ObjectMapper拿到數(shù)據(jù)。
具體使用:

provider.rx.request(.ip)
    .map(HTTBin.self)
    .subscribe { result in
        switch result {
        case .success(let element):
            guard let bin = element else { return }
            print("ip :", bin.ip)
        case .error(let error):
            print(error.localizedDescription)
        }
    }.disposed(by: bag)
打?。?ip : 101.81.57.239

這樣以來就方便并且快捷很多,非常容易擴展以及維護。

--以此來記錄 Swift Moya ^ _^ --

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容