エラー処理
シーケンスでエラーイベントが生成されると、シーケンス全体が終了します。RxSwiftには2つの主要なエラー処理メカニズムがあります。
再試行再試行キャッチ再開再試行再試行
retryは、エラー後にシーケンスが再試行できるようにします。
// JSONのリクエストが失敗した場合は、すぐに再試行します。//3回の再試行後に失敗した場合は、エラーがスローされます
let rxJson: Observable<JSON> = ...
rxJson
.retry(3)
.subscribe(onNext: { json in print("取得 JSON 成功: \(json)") }, onError: { error in print("取得 JSON 失败: \(error)") }) .disposed(by: disposeBag)
上記のコードは非常に直接的な再試行(3)です。つまり、エラーが発生すると、再試行操作が実行され、再試行は最大3回実行されます。
retryWhen
エラーが発生したときにしばらくしてから再試行する必要がある場合は、次のようにして達成できます。
// JSONのリクエストが失敗した場合、5秒待ってから再試行します。
let retryDelay: Double = 5 // 重试延时 5 秒
rxJson
.retryWhen { (rxError: Observable<Error>) -> Observable<Int> in
return Observable.timer(retryDelay, scheduler: MainScheduler.instance) } .subscribe(...) .disposed(by: disposeBag)
ここでは、retryWhen演算子を使用する必要があります。これは、主に再試行するタイミングを記述し、クロージャーで返されるObservableを介して再試行のタイミングを制御します。
.retryWhen { (rxError: Observable<Error>) -> Observable<Int> in
...
}
クロージャのパラメータは、生成可能なエラーのシーケンスであるObservableであり、戻り値はObservableです。返されたObservableが要素を発行すると、操作が再試行されます。エラーまたは完了したイベントが発生した場合、再試行せず、このイベントを後続のオブザーバーに渡します。
最大数の再試行を追加する必要がある場合:
// JSONのリクエストが失敗した場合、5秒待ってから再試行します。// 4回再試行しても失敗する場合は、エラーをスローします
let maxRetryCount = 4 // 最多重试 4 次
let retryDelay: Double = 5 // 重试延时 5 秒
rxJson
.retryWhen { (rxError: Observable<Error>) -> Observable<Int> in return rxError.flatMapWithIndex { (error, index) -> Observable<Int> in guard index < maxRetryCount else { return Observable.error(error) } return Observable<Int>.timer(retryDelay, scheduler: MainScheduler.instance) } } .subscribe(...) .disposed(by: disposeBag)
ここで達成したいのは、4回以上再試行するとエラーがスローされることです。エラーが4回以内の場合は、5秒待ってから再試行してください。
...
rxError.flatMapWithIndex { (error, index) -> Observable<Int> in
guard index < maxRetryCount else {
return Observable.error(error)
} return Observable<Int>.timer(retryDelay, scheduler: MainScheduler.instance) } ...
演算子flatMapWithIndexを使用します。これは、間違ったインデックス番号を提供する可能性があるためです。次に、このインデックス番号を使用して、最大再試行回数を超えているかどうかを判断します。超えている場合は、エラーがスローされます。超えていない場合は、5秒待ってから再試行してください。
catchError-recovery
catchErrorは、エラーが発生したときに、エラーをスペア要素またはスペア要素のセットに置き換えることができます。
searchBar.rx.text.orEmpty
...
.flatMapLatest { query -> Observable<[Repository]> in
...
return searchGitHub(query) .catchErrorJustReturn([]) } ... .bind(to: ...) .disposed(by: disposeBag)
最初のGithub検索では、catchErrorJustReturnを使用しました。エラーが発生すると、空の配列が返され、空のリストページが表示されます。
catchErrorを使用することもできます。エラーが発生した場合は、エラーイベントを別のシーケンスに置き換えます。
//最初にネットワークからデータを取得し、取得が失敗した場合はローカルキャッシュからデータを取得します
let rxData: Observable<Data> = ... // 网络请求的数据
let cahcedData: Observable<Data> = ... // 之前本地缓存的数据
rxData
.catchError { _ in cahcedData } .subscribe(onNext: { date in print("获取数据成功: \(date.count)") }) .disposed(by: disposeBag) Result
ユーザーにエラーメッセージを表示したいだけの場合、どうすればよいでしょうか。
以下は最も直接的な解決策ですが、この解決策にはいくつかの問題があります。
//ユーザーが更新ボタンをクリックすると、//変更されたユーザー情報をすぐに取り出します。//次に、更新操作を実行するためのネットワーク要求を開始します//操作が失敗すると、失敗の理由をユーザーに要求します
updateUserInfoButton.rx.tap
.withLatestFrom(rxUserInfo)
.flatMapLatest { userInfo -> Observable<Void> in
return update(userInfo)
} .observeOn(MainScheduler.instance) .subscribe(onNext: { print("用户信息更新成功") }, onError: { error in print("用户信息更新失败: \(error.localizedDescription)") }) .disposed(by: disposeBag)
これは非常に簡単です。ただし、ネットワーク要求操作が失敗すると、シーケンスは終了します。サブスクリプション全体がキャンセルされます。ユーザーが更新ボタンを再度クリックした場合、ネットワークリクエストを開始して再度更新することはできません。
この問題を解決するには、エラー処理の適切なソリューションを選択する必要があります。たとえば、システムに付属する列挙型Resultを使用します。
public enum Result<Success, Failure> where Failure : Error {
case success(Success)
case failure(Failure)
}
次に、前のコードを次のように変更する必要があります。
updateUserInfoButton.rx.tap
.withLatestFrom(rxUserInfo)
.flatMapLatest { userInfo -> Observable<Result<Void, Error>> in
return update(userInfo)
.map(Result.success) // 转换成 Result .catchError { error in Observable.just(Result.failure(error)) } } .observeOn(MainScheduler.instance) .subscribe(onNext: { result in switch result { // 处理 Result case .success: print("用户信息更新成功") case .failure(let error): print("用户信息更新失败: \(error.localizedDescription)") } }) .disposed(by: disposeBag)
このようにして、エラーイベントはResult.failure(Error)要素にラップされ、シーケンス全体が終了することはありません。ネットワーク要求が失敗した場合でも、サブスクリプション全体がまだ存在しています。ユーザーが更新ボタンをもう一度クリックすると、更新操作を実行するためのネットワーク要求を開始できます。