PromiseKitの使い方 その4
Nov 24, 2017 · iosswift3
SwiftでPromise
を使える様になるPromiseKitの使い方 その4
PromiseKit
でリトライ処理を実行する方法
非同期処理部分がネットワーク処理の様にある程度失敗することが前提で、リトライをさせたい場合の方法
まず、リトライの動きとしては、
- ある非同期処理が失敗した時に、同じ非同期処理のリトライができる
- 非同期処理は引数あり
- 最大リトライ回数を指定できる
- リトライする前に処理をはさめる
(発生したエラーによってはリトライさせないとかキャンセルするとか)
という挙動をさせる
ソース
基本は公式のリトライ方法をベースに汎用的に使える様に以下の専用メソッドを作成
動作の流れとしては、
- リトライ回数を+1
- 非同期処理を実行
- 失敗すれば、
recover
で一旦捕捉 preRetry
でリトライするかチェック
(あるいは何か必要な処理。例えば、リトライ開始を通知するなど)true
: 次の処理へfalse
: 失敗時のエラーをthrow
(このメソッドの呼び出し元でエラー処理)
- リトライ上限回数内かチェック
- 上限回数内: 1へ戻る
- 上限回数を超えた:
RetryError
をthrow
(※今回はリトライしたがエラーになったというのを知りたいので、専用のRetryError
を作っているが、単に失敗時のエラーをthrow
でも良い)
という感じになる
非同期処理に渡す引数がもし、複数ある場合は、タプルで渡してあげれば良い (次の使い方の例を参照)
使い方
例えば、「ログイン処理のAPIを3回までリトライ、ただし認証エラーの場合はリトライしない」といった挙動をさせたいなら、
firstly {
retry(maxRepeat: 3, args: ("username", "1234"), task: login, preRetry: { error -> Bool in
switch error as APIError {
case .auth: return false
default: return true
}
})
}.then { result: String in
// 以下省略
func login(username: String, password: String) -> Promise<String> {
// 以下省略
といった感じで呼び出せばOK。
もちろん、login
メソッドの中でもPromise
を使える
リトライ部分を自分で一旦実装しないといけないのが面倒だが、Promise
を使わない場合と比べて、かなりシンプルに書けているのが良いと思う
(本当はある程度フレームワーク側で実装されているとうれしいが、エラーをどうするとかの細かな部分はプロジェクトごとで違うので仕方がないのかも)
また、このリトライ自体の処理がメソッド内で完結しているのも良い。リトライ回数が内部に隠蔽されているので、誰かが勝手に回数を書き換えるみたいな妙なバグが入らない
さらに、シーケンスが複雑になってもコードが見にくくならない。
単なる通信のリトライぐらいならまだベタ書きでもなんとかなるかもしれないが、例えば、NFCの様にデータの書込や読込をさせるのに一連のコマンドのやりとりが必要で、さらにある処理をさせるにはその書込や読込を複数回行わないといけない。
また、当然電波を使っているので、コマンドのやりとり自体にもリトライ処理が必要といった場合、Promise
の威力は絶大になる
(ちなみに某案件ではベタ書きで実装されていたのをPromiseKit
で書き直すと3分の1以下にソースが減ったという実績あり。
しかもベタ書きではリトライ処理が全くの未実装だったにも関わらず・・・)
開発環境
- Xcode 8.3.3
- iOS 10.3
- PromiseKit 4.4