Как реализовать общий Intent, State и базовые классы в MVI?
Перехожу с MVVM на MVI в своем pet-проекте и пока проблемы с тонкостями реализации ;) Вводные: есть несколько общих блоков кода, которые я использую на разных экранах, затолкав их в базовые классы. Например, абстрактный фрагмент и viewModel для логики searchBar, которая используется на N экранах. MVVM имеет нестрогую организацию - я мог вызвать нужный метод из SearchViewModel в любой из реализаций, но в MVI я должен заменить этот вызов на строгий dispatchIntent. Проблема в том, что все интенты и состояния в моем понимании являются запечатанными классами, а для них вроде как нельзя создать общий класс (один на несколько экранов. В идеале, если бы я смог расшарить условный SearchIntent(val keyWord: String) с несколькими экранами. Вот код:
SearchViewModel:
abstract class SearchViewModel<INTENT : MviIntent, STATE : MviState>(defaultState: STATE) :
BaseSubscriptionViewModel<INTENT, STATE>(defaultState) {
private var keyWord: String = ""
override suspend fun dispatchIntent(intent: INTENT) {
this.keyWord = intent.keyWord ?: "" // <= Error
}
/**
* Changed key word async.
*
* @param keyWord Key word
*/
fun updateKeyWordAsync(keyWord: String?) = launchInBackground { // <= Should be replaced by dispatchIntent.
keyWordWasChanged(keyWord)
}
/**
* Key word was changed.
*
* @param keyWord Key word
*/
protected open suspend fun keyWordWasChanged(keyWord: String?) {
this.keyWord = keyWord ?: ""
}
}
Fragment:
abstract class SearchToolbarFragment<INTENT : MviIntent, STATE : MviState, VM : SearchViewModel<INTENT, STATE>>(
private val vmKClass: KClass<VM>
) : ToolbarFragment<INTENT, STATE, VM>() {
open val menuId: Int = R.menu.menu_search
private val queryTextListener = object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
viewModel.dispatchIntentAsync(INTENT(query)) // <= Error. How to resolve it?
// viewModel.updateKeyWordAsync(query)
return true
}
override fun onQueryTextChange(newText: String?): Boolean {
viewModel.dispatchIntentAsync(INTENT(query)) // <= Error. How to resolve it?
// viewModel.updateKeyWordAsync(query)
return true
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(
store = viewModelStore,
factory = viewModelFactory,
defaultCreationExtras = defaultViewModelCreationExtras
)[vmKClass.java]
}
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
super.onCreateMenu(menu, menuInflater)
menuInflater.inflate(menuId, menu)
initSearch(menu)
}
/**
* Init search.
*
* @param menu Menu
*/
private fun initSearch(menu: Menu) {
// ....
}
}
В добавок появился еще один вопрос: в какой момент вызывать initSearch(menu)? С одной стороны мне нужен объект темы, но с другой внутри у есть зависимость от текущего keyWord. PS Если есть хороший полный гайд по MVI - буду рад. На статьи с простейшими примерам я уже насмотрелся, они ответа на мои вопросы не дают.