前言
在之前的文章Moya+PromiseKit+RxSwift優(yōu)雅的書寫網(wǎng)絡(luò)請求中,我們嘗試了使用PromiseKit和RxSwift共同實現(xiàn)網(wǎng)絡(luò)請求,在后來我個人的嘗試中發(fā)現(xiàn)了問題校镐,遂撰文記之。
問題描述
PromiseKit的閉包只會執(zhí)行一次捺典。
環(huán)境配置
- Xcode 8.3
- Swift 3
實例
這一切都是由于實現(xiàn)一個帶有緩存的網(wǎng)絡(luò)請求引起的鸟廓。為此我們實現(xiàn)一個RxMoyaProvider的Extension,用于實現(xiàn)帶有緩存的網(wǎng)絡(luò)請求襟己。
extension RxMoyaProvider {
func offLineCacheRequest(_ token: Target) -> Observable<Response> {
return Observable.create({[weak self] (observer) -> Disposable in
// 1. 在這里我們讀取本地緩存中的數(shù)據(jù)引谜,若有緩存,則返回緩存數(shù)據(jù)
// 偽代碼
if 存在緩存 {
observer.onNext(緩存數(shù)據(jù))
}
//2 .進(jìn)行正常的網(wǎng)絡(luò)請求
let cancellableToken = self?.request(token) { result in
switch result {
case let .success(response):
// 3. 返回請求后的最新數(shù)據(jù)
observer.onNext(response)
observer.onCompleted()
// 4. 緩存并覆蓋舊數(shù)據(jù)
// 偽代碼
緩存數(shù)據(jù)
case let .failure(error):
observer.onError(error)
}
}
return Disposables.create {
cancellableToken?.cancel()
}
})
}
}
我們基于剛剛實現(xiàn)的這個拓展再實現(xiàn)一個網(wǎng)絡(luò)請求。注意此處成功的回調(diào)result。
func getHomepagePageDataWithCache() -> Promise<HomepageData> {
return Promise(resolvers: { (result, error) in
provider.offLineCacheRequest(.frontpage)
.distinctUntilChanged()
.filterSuccessfulStatusCodes()
.mapJSON()
.mapObject(type: HomepageData.self)
.subscribe(onNext: {
result($0) //此處為PromiseKit的成功回調(diào)
}, onError: {
error($0)
})
.addDisposableTo(disposeBag)
})
}
然鵝就在這里出現(xiàn)問題了某筐,當(dāng)我們調(diào)用這個方法時:
viewModel.getHomepagePageDataWithCache().then {
print($0.packages?.last?.head ?? "")
}.catch {
print($0)
}
第一次調(diào)用因為本地沒有緩存溃槐,所以打印print($0.packages?.last?.head ?? "")只會調(diào)用一次,然鵝再次運行哮幢,存在本地緩存的情況下,該打印語句依然只執(zhí)行一次。
經(jīng)過打斷點滑频,我發(fā)現(xiàn)相關(guān)代碼均已經(jīng)執(zhí)行:
// 請求成功前
if 存在緩存 {
observer.onNext(緩存數(shù)據(jù))
}
// 請求成功后
observer.onNext(response)
以上兩次 observer.onNext都觸發(fā)了RxSwift訂閱,斷點也會停留在訂閱里面PromiseKit的閉包result上:
.subscribe(onNext: {
result($0)
},
但是在最終的閉包里只執(zhí)行了一次打用2亍:
then {
print($0.packages?.last?.head ?? "")
}.
原因
經(jīng)過查詢資料误趴,原因如下:
PromiseKit 不具備流的特性,即不支持依賴時間順序依次傳遞值务傲,換句話說就是調(diào)用閉包 result多次也只能執(zhí)行一次凉当。這就沒辦法讓我們以完整的聲明式的寫法完成需求。
所以要想兩次都觸發(fā)并執(zhí)行RxSwift的訂閱售葡,就不能使用PromiseKit來實現(xiàn)這個網(wǎng)絡(luò)請求看杭。處理很簡單,就是把PromiseKit里面實現(xiàn)網(wǎng)絡(luò)請求的部分提出來改寫即可挟伙。
provider.offLineCacheRequest(.frontpage)
.distinctUntilChanged()
.filterSuccessfulStatusCodes()
.mapJSON()
.mapObject(type: HomepageData.self)
我們把這個mapObject后的Observable返回即可楼雹,讓后續(xù)的操作訂閱它。