久々に技術っぽいポエムを。
Qiita で Android の MVI アーキテクチャについての記事を書いている。
思ったよりも大作になってきて、ちょっとどこから編集を追記すれば良いのかなという気持ちになっていたりするのだけれど、ぼちぼち作っていきたい(また書籍化したい・・・?)。
さて、 この Qiita の記事を書くきっかけはあるプロジェクトの中で MVI アーキテクチャを採用しませんか?ということになったため。練習レベルで MVI アーキテクチャや iOS の Reactor Kit を使ったコードを書いたことはあるけれど、ちゃんとプロダクトで使ったこともないし、人に説明ができる感じでもないなという状態だったため、今一度自分のスキルセットを確認しようと思い筆を執った次第。
で、コードを書いていて思ったのだけれど全部が全部 MVI っぽい感じのコードにはならない。まあ、そんなのは当たり前で、アーキテクチャを決める会なんかのときにも「いやあ、全部が全部 MVI っぽい書き方にすると大変っすよー」みたいなことは言うのだけれど、具体的に「全部が全部」じゃない部分ってどれよ?ってのを今ならある程度言語化できそうなので、忘備録的な意味合いでちょっとまとめておく。
プロジェクトが終わった時に考え方が変わっているかどうかが見もの。ちゃんとプロジェクトが終わりますようにという思いも込めて 🙏
静的な View
そもそも MVI を導入する目的は「複雑になった画面の状態を抽象化し、コードで表現できるようにしてデバッグやテストをしやすくする」ことだと思う。その目的にそぐわないのが「静的な View」だ。一般的なアプリだと
- ヘルプ、使い方画面
- WebView だけの画面
とか。「WebViewだって、View の中身のページが変わるじゃん!」みたいな考えもできるけれど、中身のコンテンツ自体は WebView が状態を管理してくれることが多いのでそっちに任せておくのが良さそう。静的な View しか存在しない画面は MVI っぽく作る必要がないし、動的に変化をする画面でも 静的な部分については State で管理をする必要はない。
画面遷移
画面遷移も Fragment なり Activity なりを管理してくれるのは os なので、アプリの方では気にしない。ボタンを押したりしたら普通に startActivity や add/replaseFragment で画面を開けばいい。個人的には Espresso でテストがしやすいので EventBus 的に Fragment → Activity で通信を行う仕組みを作っていると、EventBus をモック化することで Fragment の UI テストがしやすくなるので好み。ただしこれは MVI とは関係がない。
Runtime Permission
これもなー、画面遷移に近いのかなーって思っている。ボタンを押して現在位置を取得する、みたいな実装の場合
- ボタンを押す
- リスナーで Runtime Permission のチェック
- Permission の状態に応じて ViewModel に対して Intent を飛ばす
のように、View 側で Permission の管理をしちゃうのが良さそう。Runtime Permission 関連の API が Context に依存していてさらに画面上にダイアログなんかも出ちゃうので、このダイアログを State で管理をするとかやりたくないよね?って感じ。
ダイアログ、 Toast、 SnackBar
これに関してはまだよくわからない。 State に isShowDialog: Bool
なフラグをもたせておいて、 True なら表示をすればいいのだけれど、じゃあいつ False になるんだろうか?Toast や SnackBar も同じ。これらの View が「閉じた」というイベントをトリガーにしてフラグを倒すのかな。SnackBar は簡単にできそうだけれどダイアログと Toast は大変そう。 Droidkaigi でも Toast に関しては「表示してからn秒後にフラグを倒す Intent を発行する」とか言ってて、やべえなって思った。ダイアログに関してはダイアログが閉じられるケースを全部網羅するの?まじで?って気持ちもある。
画面遷移に近いようなきもするけれど、こういう UI を出したいタイミングって非同期処理なりビジネスロジックの終了をトリガーに行うことが多いイメージ。なので「ボタンを押したらすぐ表示」というわけにはいかないよね。
大変そう。頑張ってフラグで管理をするのかな。