どこからでもliff.init()の完了を待てるliff.readyと、その注意点
今回のTipsでは、地味ながら便利なプロパティliff.readyと、その使い方の注意点を紹介します。
liff.init()の完了をどこで待つか
LIFFアプリでは、liff.getProfile()メソッドをはじめ多くのメソッドが、liff.init()メソッドの完了後にしか呼べません。アプリの規模が小さいうちはliff.init().then(...)の中にすべての処理を書けば済みますが、処理がモジュールやファイルに分かれてくると、イベントハンドラやユーティリティ関数の側でも「liff.init()の完了後であること」を保証したくなります。
そのための仕組みは自分で用意することもできます。たとえば、liff.init()が返すPromiseをexportして共有したり、初期化が完了したかどうかのフラグを自前で管理したりする方法です。一方で、LIFF SDKにも組み込みのプロパティが用意されています。それがliff.readyです。
liff.readyでliff.init()の「呼ぶ場所」と「待つ場所」を分離する
liff.readyは、liff.init()の実行が成功したときにresolveされるPromiseを保持するプロパティです。liff.init()を呼ぶ前から参照できるため、「liff.init()はアプリのエントリポイントで1回だけ呼ぶ。その完了を待ちたい場所ではliff.readyをawaitする」という分離ができます。
liff.readyの利点は手軽さです。Promiseやフラグを自前で配線しなくても、import済みのliffオブジェクトからそのまま完了を待つことができます。liff.init()の完了より先にloadProfile()が呼ばれても、liff.readyが解決されてから後続の処理が実行されます。
liff.readyは「成功」しか通知しない
ここからが本題です。『LIFF APIリファレンス』の「liff.ready」には次のように書かれています。
liff.init()実行中に何か問題が起きても、liff.readyはrejectしません。また、LiffErrorオブジェクトを返すこともありません。
つまり、LIFF IDの設定ミスやネットワークエラーでliff.init()が失敗すると、liff.readyはrejectされるのではなく、いつまでも解決されないまま残ります。先ほどのloadProfile()でいえば、await liff.readyの先には進みません。このときliff.ready側にはエラーが通知されないため、エラー処理をliff.readyに期待していると、「画面が表示されないが、コンソールにもそれらしいエラーがない」という状況になり、原因に気づきにくくなります。
liff.readyは「liff.init()が完了したか」だけを伝えるシンプルな完了シグナルで、エラーハンドリングの責務は持っていません。エラー処理はliff.init()が返すPromiseの側で行ってください。最初のコード例で、エントリポイントのliff.init()に.catch(handleInitError)を付けていたのはこのためです。初期化の失敗は、このcatchにしか通知されません。handleInitErrorでエラー画面への切り替えなどを行っておけば、liff.readyを待つ側が止まったままでも、ユーザーに何も表示されないという事態を避けることができます。
役割分担は以下のとおりです。
| やりたいこと | 使うもの |
|---|---|
liff.init()の完了を任意の場所で待つ | liff.ready |
liff.init()の失敗をハンドリングする | liff.init()が返すPromiseのcatch |
なお、「liff.init()の完了を待つ場所」で失敗も知りたい場合は、liff.readyではなくliff.init()が返すPromiseを共有してawaitしてください。こちらは失敗時にrejectされます。また、アプリの状態管理にliff.init()の成否を載せる方法もあります。失敗のハンドリングが各所で必要なアプリでは、最初からPromiseを共有する設計のほうが向いています。liff.readyはあくまで「成功だけを手軽に待つ」ためのプロパティです。
プラガブルSDKでもそのまま利用できます
liff.readyはSDKのコアに含まれているため、バンドルサイズ削減のためにプラガブルSDK(@line/liff/core)を使っている場合でも、追加モジュールなしでそのまま利用できます。
まとめ
liff.readyを使うと、liff.init()の完了を自前の配線なしで手軽に待つことができます。- ただし、
liff.readyは失敗を通知しません。エラー処理はliff.init()が返すPromiseのcatchで行ってください。 - 待つ場所で失敗も知りたい場合は、
liff.init()が返すPromiseを共有するか、アプリの状態管理に成否を載せる方法を検討してください。
詳しくは、『LIFF APIリファレンス』の「liff.ready」を参照してください。