1
/
5

エンジニアブログ「献立詳細画面で使っていたFlowの処理をtransformオペレータで書き直した日記」

こんにちは、AndroidエンジニアなのかFlutterエンジニアなのか蕎麦屋なのか分からない男、そば屋です。

献立機能を強化しよう!と言うことでこれまでWeb(WebView)限定となっていた「献立検索」を ネイティブ実装しました。

するとどうでしょう、前回の記事に書いた献立詳細画面にもう一パターン別のAPI呼び出しが入ると言うではありませんか

前回同様に

private val menuResponse = repository.getMenu(menuId).stateIn(viewModelScope, SharingStarted.Lazily, null)
    private val aiResponse = repository.getAiMenu(menuId).stateIn(viewModelScope, SharingStarted.Lazily, null)
    private val searchResponse = repository.getSearchMenu(menuId).stateIn(viewModelScope, SharingStarted.Lazily, null)

のようにSharingStarted.Lazilyにしてしまい。

購読側で

when (献立のタイプ) {
    自分で作った -> 元からの処理(menuResponseを購読)
    AI -> 前回作った処理(aiResponseを購読)
    献立検索 -> 今回作る処理(searchResponseを購読)
}

とすれば別に動作に問題は起こりません

が、購読側のUI処理でEpoxyのControllerにデータを渡すのに 一度、3個に分かれて購読処理を呼び、再度共通のUI処理を呼ぶとエレガントじゃない気がします。

さらにパターンが今後も増える不安を感じる事も事実としてあります。

ふと、DroidKaigiにFlowでセッション応募したことで再勉強を始めた時に書いた記事に transformオペレータと言うものがあった事を思い出しました。これで勝つる

ViewModelの実装をtransformオペレータを使って書き直す!

Before

class ViewModel(val menuId: Int) : ViewModel(), KoinComponent {
    private val repository: Repository by inject()
    private val menuResponse = repository.getMenu(menuId).stateIn(viewModelScope, SharingStarted.Lazily, null)
    private val aiResponse = repository.getAiMenu(menuId).stateIn(viewModelScope, SharingStarted.Lazily, null)
}

After

class ViewModel(val menuId: Int) : ViewModel(), KoinComponent {
    private val repository: Repository by inject()
    private val menuType = MutableStateFlow<MenuType?>(null)

        val menuResponse: StateFlow<Response<MenuResponse>?> = menuType.transform { value ->
            val respnse = when (value) {
                MenuType.AI -> menuRepository.getAiMenu(menuId)
                MenuType.MENU_SEARCH -> menuRepository.getSearchMenu(menuId)
                MenuType.MINE -> menuRepository.getMenu(menuId)
                else -> null
           }
           emit(respnse)
        }.stateIn(viewModelScope, SharingStarted.Eagerly, null)

       fun setMenuType(menuType: MenuType) {
           menuType.value = menuType
        }
}

・enumで献立の種類を表すMenuTypeを新設
・MenuTypeをセットするStateFlowであるmenuTypeを新設
・menuTypeが変更された時に実行されるようにtransformオペレータをセット
・献立の種類(MenuType)によって呼び出すAPIを切り替えて結果をemit

Fragment

viewModel.setMenuType(MenuType.AI)

viewModel.menuResponse.flowWithLifecycle(viewLifecycleOwner.lifecycle).onEach {
    // UI処理
}.launchIn(viewLifecycleOwner.lifecycleScope)

FragmentはSafeArgsで受け取った献立の種類をViewModelに教えてあげるだけで、 勝手に適当なAPIを呼び出して結果を返してもらえるので、購読先の分岐もなくなりスリムになりました。

最後に

完全にオシャレかつエレガントに作ることができました。 少し(かなり?)背伸び気味にDroidKaigiのセッションに応募し、 焦って学び直した結果生まれたコードなので学ぶと結果は返ってくると言う証明になりました。 ※セッションは不採択となってしまいましたが、勉強をしなおすきっかけになったので良い経験でした。筋肉は裏切らない。

おいしい健康では、モバイル(iOS/Android)、Web、機械学習と様々な職種のエンジニアが働いています。エンジニアブログでは、おいしい健康のエンジニアメンバーが日々どんな課題に向き合っているのかを綴っています。過去のブログもありますので、ご興味ある方はぜひこちらも覗いてみてください。

https://oishi-kenko.hatenablog.com/

If this story triggered your interest, why don't you come and visit us?
Androidに転向したいエンジニア歓迎!食と健康のtoC向けアプリ開発
おいしい健康's job postings
5 Likes
5 Likes

Weekly ranking

Show other rankings
Like Maasa Noguchi's Story
Let Maasa Noguchi's company know you're interested in their content