これまでは問題なかったんですが、機能の追加でAPIリクエストを全部待ってから次の処理をしたくなりました。なのでPromise系のライブラリを導入して、きちんと待機することにしました。
Promise系のライブラリはいくつかあるようです。
代表格はスター数、10,000越えのPromiseKit
か。
でもREADMEを読んでしっくり来たのはこっちだったので、Hydra
というのを使いました。
まず既存コード
かなりメタ化しちゃってますが、既存コードはこんな感じでした。
func reload() {
API.getData1() { data1 in
if let data = data1 {
Data.setData1(data)
}
}
API.getData2() { data2 in
if let data = data2 {
Data.setData2(data)
}
}
}
元々はこれで問題ありませんでした。ですが、全てのAPIが返ってきたあと次のページへ遷移するケースが発生しました。
きちんと全API呼び出しを待たないと、データが半端な状態でページ遷移をしてしまうことになります。Promiseの出番です。
Hydraを導入する
cocoapodsで導入しました。プロジェクトがSwift3系だったので、pod 'HydraAsync', '~> 1.0.2'
して導入します。
API呼び出しを書き換える
元々のコードを次のように分離します。
import Hydra
func reload() {
let promise1 = getData1()
let promise2 = getData2()
}
func getData1() -> Promise<Void> {
return Promise<Void>(in: .background, token: nil) { (resolve, reject, _) in
API.getData1() { data1 in
if let data = data1 {
Data.setData1(data)
resolve()
} else {
reject("error")
}
}
}
}
func getData2() -> Promise<Void> {
// 略...
}
Promise<Void>
の最初の引数はiOSのDispatchQueue関連に対応するenumです。多くは.background
になるんじゃないかな。
2番目はInvalidationTokenでキャンセルするときに使うっぽい。今回は要らないから使ってなくて、よくわかってない。今回はnil
にしてます。
最後のBlockがPromiseの実装を書くところ。
READMEのCreate a Promiseを参考に、元々のAPI呼び出しを移植してresolve
とreject
を入れました。
今回はこの箇所でのみPromiseを使いたかっただけなので、API呼び出し自体をPromiseを返す実装に変更してしまうのはやめました。他の箇所への影響を考えてのことです。
逆にいうとAPI呼び出ししてPromiseを返す関数を1個挟むだけで使えるようになるので、小さく試すには便利。
完了を待つ
これはドキュメントのall
の項目のとおり。
さっきの例だと、
func reload() {
let promises = [getData1(), getData2()]
all(promises).then { _ in
self.doSomething()
}.catch { err in
}
}
こうなります。すっきり書けました。