🧭

モバイルアプリ開発と単方向データフローの話

2021/08/22に公開

Zenn初投稿!Unidirectional Data Flow(以下、単方向データフロー)についてです。

Web Frontendの方には馴染み深いと思いますが、モバイルアプリ開発ではTwo-way data binding(以下、双方向データバインディング)が主流でした。

特にAndroidはData BindingやView Bindingが公式から出ているためその傾向は顕著で、iOSもRx系を使って擬似的or結果的に双方向を実現しているケースが多いです。

ですが、最近になってSwiftUI/CombineやJetpack Composeなどで、WebのFlux系に似たような単方向データフローが浸透し始めているようです。

ここでは、主にモバイル開発者向けにそれを一度おさらいしようと思います。

双方向データバインディングとは

まず、対の概念である双方向データバインディングですが、ある値がユーザー→Viewの入力に連動し、また通信結果などにも連動している状態のことでした。値はViewModelに置かれることが多いです。

View⇄ViewModel⇄UseCase

このように各境界が双方向に繋がっており、UseCaseの変更がViewに、Viewの変更がUseCaseに伝播しやすく、責務を分割しながらコードを整理できるのが特徴です。

見かけのコード量は少なくできますが、各境界でのデータ型の変換や、ビューパーツごとにバインディングするための実装が必要になるため、全体のコード量は減らしづらい性質があります。

単方向データフローとは

かたや単方向データフローは、ユーザーがViewに与えた変更をActionとして受け取り、値は直接更新しません。 Actionはロジックを経てStateを更新し、StateがViewに描画されるというサイクルの関係です。

View→Action→State→View

単方向データフローでは、データは常に同じ方向へ流れ、逆流は許されません。ActionはViewからの命令、StateはActionの結果、そしてViewはStateの結果です。

単方向にするメリットとは

一見すると双方向のほうが便利でとっつきやすい印象ですが、実は単方向化には色々とメリットがあります。

  1. 単一化/カプセル化
  • MutableなStateが複数箇所に散らばるのを防ぐ
    • 双方向の場合、どこに置くかは曖昧である
  1. 共有
  • 1つのStateを複数の子Viewで使いまわしやすい
    • 双方向の場合もできるが、Viewを越えて共有するための実装が複雑化する
  1. 分離
  • Stateが更新されたときの影響を最小限に留められる
    • 双方向の場合、スレッドなどの考慮が必要
  • データの流れが単方向なので、切り出しや拡張が容易
  • データの流れが単方向なので、デバッグやテストがしやすい

もちろんメリットだけでなく、デメリットも存在します。

  1. 単一化/カプセル化
  • MutableなStateが1箇所なので、適用できるデザインパターンが限られる
  1. 共有
  • Viewライフサイクルの細かい考慮が必要
    • 開いていないViewのStateまでメモリ上に持つのは理想的ではない
  1. 分離
  • Viewのコード量、コンポーネントが増える
    • パーツごとに描画用のVariableと入力のListenerを毎回書く必要がある
    • MVVMやMVCに慣れた人が理解しづらかったり、プロジェクトごとに実装スタイルの差が出やすい

Androidアプリの変化

Androidアプリの場合、今までは@BindingAdapterがよく使われており、推奨アーキテクチャがMVVMなことからも、双方向データバインディングが暗黙的に推奨されていたと言えます。

これはアプリ規模が大きくなるにつれ、アプリ独自のバインディングが多数実装されることを意味しました。やりすぎるとブラックボックス化が起きたり、Android初心者にとって学習ハードルが高い部分でした。

また、接続がアノテーション任せな上に双方向なので、デバッグやバグ調査がしづらい問題もありました。

2021年7月、待望のJetpack Composeが登場し、単方向データフローが公式推奨されました。これを導入すると、UIからStateを直接変えれなくなるため、先述の複雑性からある程度解放されます。

また、デメリットの1つであるライフサイクル問題も、remember宣言子によってStateの生存期間をViewライフサイクルに合わせられるので回避できます。

iOSアプリの変化

iOSアプリは、MVCやMVC+VMのようなアーキテクチャがよく使われてきました。

しかし、Rx系をふんだんに使わないとあっという間にControllerが肥大化したり、外部のフレームワークに頼らざるをえないのが積年の問題でした。一部をRouterに切り出したり、VIPERを導入したりなどの模索が続きました。

2019年、Swift UI/Combineが登場し、こちらも単方向データフローが公式サポートのスタート地点に立ちました。非公式ですがTCAも登場し、着々と置き換えが進んでいる印象です。

おわりに

この記事は単方向データフローを推奨するものというより、近年のトレンドに対して私個人の見解を述べたものです。もし他にこういう観点があるとかコメントなどあれば、ぜひ教えてください。

次の記事

Discussion