dip Engineer Blog

Engineer Blog
ディップ株式会社のエンジニアによる技術ブログです。
弊社はバイトル・はたらこねっとなど様々なサービスを運営しています。

dip×RIZAP iOS/Androidアプリ開発勉強会 UI stateについて発表しました!

はじめに

こんにちは!ディップ株式会社で新卒2年目のAndroidエンジニアをやっている氏家拓海(@southcloud_7960)と申します。 今回はdip×RIZAPでモバイルアプリ勉強会を行ったので、その参加レポートを書きたいと思います! イベントのページはこちらです!

会場の雰囲気

発表の会場がこんな感じです!自社プロダクトの開発に関する発表や、技術に関する掘り下げ、CfPの分析についてなど幅広い発表内容でした。私は最前列で大きく頷きながら聞いておりました。

懇親会のご飯の写真がこちらです!美味しいご飯があると話も弾みますね!

LT発表の内容について懇親会で掘り下げて話すこともできたので、オフラインイベントならではの楽しい時間を過ごすことができました。イベントに参加するたび友達が増えるので、みなさんもぜひぜひ参加してみてください。モバイル系のオフラインイベントには高確率で私も参加してます。

発表した内容

では、今回私が発表した内容についてご紹介したいと思います。主にAndroidエンジニアの方向けとなっております。 今回私が発表した内容は、「UI state」と呼ばれる概念についての掘り下げです。

つまりUI stateとはなんなのか?

UI stateがわからなかった

みなさんは「UI stateってなに?」と聞かれたら、どう答えますか?

Androidエンジニアとして駆け出しの頃(今もですが)、Jetpack Composeを使って書かれたコードを読んでいると、しばしばuiState(〇〇UiState)という変数やクラスを見かけました。しかし、最初はなんのことだかいまいち理解できませんでした。

「UI stateとは」で検索してもはっきりとした答えが得られませんでしたし、経験の浅い当時の私には、Googleの公式の説明は何を言っているのかいまいち理解できなかったからです。

「UI stateはdata classで状態をまとめたもの」と一度は納得したものの、その後も根気強く調べていく中で、UI Stateというものはどうにも抽象度の高い言葉で、一言では説明しにくいことがわかりました。どこが理解しにくかったのか、最終的にどう理解したのかをご紹介したいと思います。

UI Stateとは

Googleの公式ドキュメントを追ってみましょう。

UI state is the property that describes the UI

日本語(サイトの日本語訳そのまんま)だと…

UI 状態は UI を表すプロパティです。

そのまんまやないかい!

もちろんその通りではあるんですが、UI stateっていうのは一言で表現にするにはどうも広くUIの状態について扱う概念らしく、「つまりUI stateとはなんなのか?」に対する回答はなかなか難しいようです。

公式ドキュメントでは以下のように続きます。

UI state is the property that describes the UI. There are two types of UI state:

ふむふむ。どうやら2種類あるようですね。ということで、それらを解説していこうと思います。

1. Screen UI state

まずは公式のドキュメントの記載を。

Screen UI state is what you need to display on the screen. For example, a NewsUiState class can contain the news articles and other information needed to render the UI. This state is usually connected with other layers of the hierarchy because it contains app data.

画面 UI 状態: 画面に表示する必要があるもの。たとえば NewsUiState クラスには、ニュース記事や、UI のレンダリングに必要なその他の情報を含めることができます。この状態にはアプリデータが含まれるため、通常は階層の他のレイヤに接続されます。

私はこの説明だけだとピンときませんでした。画面に表示する必要があるものと言っても、いろいろあるじゃないかと。もう少し具体例がほしいところですが、ドキュメントでこれ以上の説明が見つけられませんでした。せっかくなので公式のサンプルアプリJetNewsから、NewsUiStateクラスのどのプロパティがScreen UI stateなのか考えてみます。

data class InterestsUiState(

    val topics: List<InterestSection> = emptyList(),

    val people: List<String> = emptyList(),

    val publications: List<String> = emptyList(),

    val loading: Boolean = false,

)

topics、people、publicationsについては、それぞれ画面で表示する情報だということがわかりやすいと思います。タブで切り替え可能で、タブごとにリスト形式で情報を表示しています。迷いなくScreen UI stateといって問題ないでしょう。

では、loadingはどうでしょうか?少し考えてみてもらえると嬉しいです。

いったんUI element stateの説明に移ります。

2. UI element state

UI element state refers to properties intrinsic to UI elements that influence how they are rendered. A UI element may be shown or hidden and may have a certain font, font size, or font color. In Android Views, the View manages this state itself as it is inherently stateful, exposing methods to modify or query its state. An example of this are the get and set methods of the TextView class for its text. In Jetpack Compose, the state is external to the composable, and you can even hoist it out of the immediate vicinity of the composable into the calling composable function or a state holder. An example of this is ScaffoldState for the Scaffold composable.

UI 要素状態: レンダリング結果に影響する UI 要素固有のプロパティ。UI 要素は、表示または非表示となり、特定のフォント、フォントサイズ、またはフォントの色となる可能性があります。Android ビューでは、ビューは本質的にステートフルであるため、ビューがこの状態自体を管理し、状態を変更またはクエリするためのメソッドを公開します。たとえばテキストの場合、TextView クラスの get メソッドや set メソッドです。Jetpack Compose では、状態はコンポーザブルの外部にあります。コンポーザブルのすぐ近くから、呼び出し元のコンポーズ可能な関数または状態ホルダーにホイスティングすることもできます。たとえば Scaffold コンポーザブルの場合、ScaffoldState です。

個人的にはこっちの方が少しわかりやすい、理解しやすいんじゃないかと思いました。例もわかりやすいです。ScaffoldStateは、例えばDrawerの開閉状態を保ちます。UI固有の状態というのは、何の情報を表示するかではなく、その情報をどう表示するかについてです。

  • 「Drawer」が「オンかオフか」
  • 「文字」の「フォントサイズがいくつか」

そもそも日本語で「状態」って言うと、オンオフとか、どんな色だとか、どんな大きさだとか、の方がしっくりくると思うんですよね。

UI Screen stateの方は、「状態」というより「情報」な気がしていて、この辺の日本語のニュアンスに引っ張られて私は理解するのにだいぶ苦しみました。

では、改めて考えてみましょう。

loadingはScreen UI stateでしょうか?UI element stateでしょうか?

data class InterestsUiState(

    val topics: List<InterestSection> = emptyList(),

    val people: List<String> = emptyList(),

    val publications: List<String> = emptyList(),

    val loading: Boolean = false,

)

私はloadingをScreen UI stateと考えました。loadingはローディングのインジケーターを表示するための情報だと言えます。「UIのレンダリングに必要なその他の情報」ですね。

もしくは画面そのものをUI elementと捉えることで、画面の状態が切り替わることを示すUI Element stateと考えることもできるかもしれません。 みなさんはどう考えますか?ぜひ教えてください。

※実際にJetnewsのInterestsScreenでインジケーターは実装されていませんでしたが、仮にそういう画面が表示されるものと仮定します。

UI stateはdata classで状態をまとめたもの?

サンプルアプリやOSSのコードを読んでいて自分はこう解釈していた時期がありました。これは「そう使われることはあるが、そうとは限らない」といった意味でNOだと今は考えています。

UI stateをどう扱うかについて厳密な規約はなく、UI stateがどうロジックと関わるかやどう抽象化するかの方針によっても異なります。

公式ではプレーンなクラスで管理する方法や、UI(Composable)の中で扱う方法についても触れています。

他にもSealed classを使う場合や、interfaceを使う場合もあり、状態の扱いには幅広く選択肢があると思います。

つまりUI Stateってなに?

UI stateは奥が深いので、一言で「こういうものです」と答えることはできませんでした。data classで表示する情報をひとまとめにして使われることもあれば、Composableの中で使われることもありました。あえて一言で言えば「UIに関する情報」かもしれませんが、文脈によっても使われ方が違うため、やはり一言で説明しきれないと感じます。

さいごに

「こういう風に解釈してる」「こういう管理の仕方がある」など、みなさんの状態管理に関するご意見・感想を、ぜひコメントやXの引用リポストなどで頂けますと嬉しいです!

参考