provider.rx.request 方法
下面是Moya
為對RxSwift
提供擴(kuò)展的源碼鸣驱,它的實(shí)現(xiàn)使得我們可以通過provider.rx.request(api)
的方式進(jìn)行網(wǎng)絡(luò)請求妥畏。
request
方法的原理是,創(chuàng)建一個(gè) Single
類型對象(只發(fā)送一個(gè)元素的 Observable
)藕届,很顯然這是一個(gè) Cold Observable朋鞍,當(dāng)他被訂閱或者由其他事件觸發(fā)時(shí)中剩,provider
會(huì)執(zhí)行網(wǎng)絡(luò)請求方法诫咱。當(dāng)網(wǎng)絡(luò)請求返回?cái)?shù)據(jù)時(shí),single
通過判斷返回結(jié)果的類型來發(fā)送對應(yīng)的事件:result
為.success
時(shí)刃唤,就發(fā)送.success
事件(是對.next
的封裝)隔心;為.failure
時(shí),就發(fā)送.error
事件尚胞。
public extension Reactive where Base: MoyaProviderType {
/// Designated request-making method.
///
/// - Parameters:
/// - token: Entity, which provides specifications necessary for a `MoyaProvider`.
/// - callbackQueue: Callback queue. If nil - queue from provider initializer will be used.
/// - Returns: Single response object.
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()
}
}
}
}
上面的方法返回了一個(gè)Single<Response>
硬霍,然后可以通過mapJSON()
方法將Response
元素轉(zhuǎn)為字典或數(shù)組。
封裝 ObjectMapper
mapJSON
后獲得的字典或數(shù)組可以通過map
方法笼裳,進(jìn)一步轉(zhuǎn)化為 Mappable
模型或元素為Mappable
模型的數(shù)組唯卖。解析出錯(cuò)時(shí),就拋出error
躬柬,這里我的做法是拜轨,定義一個(gè)特殊數(shù)值的code
,表示服務(wù)器的數(shù)據(jù)返回有誤允青。
extension Observable {
/// 將字典轉(zhuǎn)為model
public func mapObject<T: Mappable>(type: T.Type) -> Observable<T> {
return self.map { response in
guard let dict = response as? [String: Any] else {
throw self.parseError(response: response)
}
guard let model = Mapper<T>().map(JSON: dict) else {
throw self.parseError(response: response)
}
return model
}
}
/// 將字典數(shù)組轉(zhuǎn)為model數(shù)組
public func mapArray<T: Mappable>(type: T.Type) -> Observable<[T]> {
return self.map { response in
guard let array = response as? [[String: Any]] else {
throw self.parseError(response: response)
}
return Mapper<T>().mapArray(JSONArray: array)
}
}
/// 拋出解析的錯(cuò)誤
private func parseError(response: Any) -> MoyaError {
if let _response = response as? Response {
return MoyaError.statusCode(_response)
}
// 解析失敗時(shí)橄碾,作為服務(wù)器響應(yīng)錯(cuò)誤處理
let data = try? JSONSerialization.data(withJSONObject: response, options: [])
let _response = Response(statusCode: 555, data: data ?? Data())
return MoyaError.statusCode(_response)
}
}
Single 擴(kuò)展
如果想在網(wǎng)絡(luò)請求返回Response
以后做些統(tǒng)一的操作,例如后臺(tái)規(guī)定假如返回一個(gè)code
字段,數(shù)值等于4000時(shí)法牲,表示用戶登錄的Token
失效史汗,需要退出登錄,那么可以模仿mapJSON
的做法拒垃,對Single<Response>
添加一個(gè)擴(kuò)展方法來處理淹办。然后結(jié)合上面的mapObject
和mapArray
方法,就可以愉快地使用moya
進(jìn)行鏈?zhǔn)降鼐W(wǎng)絡(luò)操作了恶复。
extension PrimitiveSequence where TraitType == SingleTrait, ElementType == Response {
func mapObjectResponse<T: Mappable>(type: T.Type) -> Observable<T> {
return self.mapResponse(complete: complete).mapObject(type: type)
}
func mapArrayResponse<T: Mappable>(type: T.Type) -> Observable<[T]> {
return self.mapResponse(complete: complete).mapArray(type: type)
}
private func mapResponse() -> Observable<Any> {
return self.map({ (res) in
// 處理Response
handleResponse(response: res)
return res
})
.mapJSON()// Response 轉(zhuǎn) Foundation 對象
.debug()
.asObservable()
}
}
使用范例:
provider.rx.request(HomeAPI.home)
.mapObjectResponse(type: HomeModel.self)
.subscribe(onNext: { (model) in
print("請求成功", model)
}, onError: { (err) in
print("請求錯(cuò)誤", err)
}, onCompleted: {
print("請求結(jié)束")
}).disposed(by: disposeBag)
如何取消請求操作?
日常使用中速挑,網(wǎng)絡(luò)請求一般是由其他事件觸發(fā)的谤牡,例如點(diǎn)擊一個(gè)搜索按鈕:
_ = button.rx.tap
.flapMap { _ in provider.rx.request(MyAPI.search) }
如果在搜索結(jié)果未返回之前,搜索條件改變了姥宝,我們需要觸發(fā)新的請求翅萤,那么這時(shí)候就有必要取消掉上次的網(wǎng)絡(luò)請求±奥回頭看看rx.request
方法套么,在源碼中,返回Disposables
時(shí)碳蛋,同時(shí)對當(dāng)前的網(wǎng)絡(luò)請求進(jìn)行了cancel
操作胚泌。
return Disposables.create {
cancellableToken?.cancel()
}
所以我們在新的請求出現(xiàn)時(shí),只需要讓上一次的請求事件dispose
掉就好了肃弟。而這里我們可以使用flatMapLatest
來實(shí)現(xiàn)玷室。flatMapLatest
等于flatMap
加switchLatest
的效果。
_ = button.rx.tap
.flapMapLatest { _ in provider.rx.request(MyAPI.search) }
有時(shí)候我們只需要取消一個(gè)請求操作笤受,這時(shí)肯定有一個(gè)取消的事件穷缤,例如cancelEvent = Variable(true)
,當(dāng)這個(gè)事件發(fā)生時(shí)箩兽,就表示我們要主動(dòng)取消請求津肛,使用takeUntil
即可。
_ = provider.rx.request(MyAPI.search).
.takeUntil(cancelEvent)