この例は moyaとrxswiftを使用してトークンを更新する方法oauthトークン で、コンパイルするために少し変更する必要がありました。このコードは私のシナリオでは80%機能します。問題は、401エラーだけでなく、すべてのhttpエラーに対して実行されることです。必要なのは、他のすべてのhttpエラーをエラーとして渡して、他の場所で処理し、ここで飲み込まないようにすることです。
このコードでは、HttpStatus 500
を取得すると、認証コードが3回実行されますが、これは明らかに私が望んでいることではありません。
401
エラーのみを処理するようにこのコードを変更しようとしましたが、何をしてもコードをコンパイルできないようです。戻り値の型"Cannot convert return expression of type Observable<Response> to return type Observable<Response>"
が間違っているといつも不平を言っていますが、これは私には意味がありません。
私が欲しいもの:401を処理しますが、他のすべてのエラーで停止します
import RxSwift
import KeychainAccess
import Moya
public extension ObservableType where E == Response {
/// Tries to refresh auth token on 401 errors and retry the request.
/// If the refresh fails, the signal errors.
public func retryWithAuthIfNeeded() -> Observable<E> {
return self.retryWhen {
(e: Observable<ErrorType>) in
return Observable.Zip(e, Observable.range(start: 1, count: 3), resultSelector: { $1 })
.flatMap { i in
return AuthProvider.sharedInstance.request(
.LoginFacebookUser(
accessToken: AuthenticationManager.defaultInstance().getLoginTokenFromKeyChain(),
useFaceBookLogin: AuthenticationManager.defaultInstance().isFacebookLogin())
)
.filterSuccessfulStatusCodes()
.mapObject(Accesstoken.self)
.catchError {
error in
log.debug("ReAuth error: \(error)")
if case Error.StatusCode(let response) = error {
if response.statusCode == 401 {
// Force logout after failed attempt
log.debug("401:, force user logout")
NSNotificationCenter.defaultCenter().postNotificationName(Constants.Notifications.userNotAuthenticated, object: nil, userInfo: nil)
}
}
return Observable.error(error)
}.flatMapLatest({
token -> Observable<Accesstoken> in
AuthenticationManager.defaultInstance().storeServiceTokenInKeychain(token)
return Observable.just(token)
})
}
}
}
}
コンパイルエラーがある行はどれですか?それはこの行になるように私には思えます:
_.catchError {
error in
//...
return Observable.error(error) // is this the line causing the compilation error?
}
_
もしそうなら、それはおそらくcatchError
がブロックが_Observable<Response>
_ではなくエラーの場合に続行できる_Observable<ErrorType>
_を返すことを期待しているためです。
どちらの場合でも、このような問題を特定できるように、コードにさらに多くの型で注釈を付けるのに役立ちます。また、Swiftコンパイラーは、この種のことを理解できないことがよくありますだから、このようなものがあなたを助けたでしょう:
_.catchError {
error -> Observable<Response> in
//...
return Observable.error(error) // Swift should have a more accurate and helpful error message here now
}
_
エラーとは何か、Xcodeを取得してより適切なエラーメッセージを表示する方法のみを示していることに注意してください。あなたが返そうとしているものはまだ正しくありません。
401
_でのみ再試行してくださいこのコードが_401
_を異なる方法で処理することを期待している理由がわかりません(通知センターへの投稿とログ記録以外)。現状ではエラーをキャッチしていますが、常にObservable
を返します。最後にError
イベントがあります(return Observable.error(error)
)ので、決して返されません。リトライ。
_401
_を再試行するには、Observable
ブロックからretryWhen
を返す必要があります。これにより、Next
イベントが送信されます(再試行することを示します)。他のすべてのステータスコードの場合、そのObservable
はError
を送信する必要があります(現在行っているように)。これは、しない再試行することを意味します。そして、エラーを伝播させたいこと。
だからこのようなもの:
_.retryWhen { errorObservable -> Observable<ErrorType> in
log.debug("ReAuth error: \(error)")
if case Error.StatusCode(let response) = error where response.statusCode == 401 {
log.debug("401:, force user logout")
NSNotificationCenter.defaultCenter().postNotificationName(Constants.Notifications.userNotAuthenticated, object: nil, userInfo: nil)
// If `401`, then return the `Observable<ErrorType>` which was given to us
// It will emit a `.Next<ErrorType>`
// Since it is a `.Next` event, `retryWhen` will retry.
return errorObservable
}
else {
// If not `401`, then `flatMap` the `Observable<ErrorType>` which
// is about to emit a `.Next<ErrorType>` into
// an `Observable<ErrorType>` which will instead emit a `.Error<ErrorType>`.
// Since it is an `.Error` event, `retryWhen` will *not* retry.
// Instead, it will propagate the error.
return errorObservable.flatMap { Observable.error($0) }
}
}
_
catchError
の場合、401エラーでない場合は、単にエラーをthrow
する必要があります。これにより、エラーがパイプに送信されます。