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