KMM Как быстро использовать дженерики kotlin?
Извините за мой плохой английский :(
Я хочу создать приложение с KMM и максимизировать общий код. И только то, что я хочу написать дважды, - это UI и Navigation. Итак, я создаю ViewModel в общем коде.
open class ViewModel<T>(initialState: T) where T : ViewState {
private val _state = MutableStateFlow(initialState)
val currentState: T
get() = stateFlow.value
val stateFlow: StateFlow<T> = _state
fun updateState(
context: CoroutineContext = EmptyCoroutineContext,
mapping: (currentState: T) -> T
) { GlobalScope.launch(context) { _state.emit(mapping(_state.value)) } }
}
Где ViewState просто
interface ViewState
который используется для реализации в запечатанном классе, который описывает текущее состояние представления. Когда он изменяется, пользовательский интерфейс получает изменения и обновления. Состояние может выглядеть так (первый бесплатный общедоступный API, который я нашел, возвращает случайное изображение собаки):
sealed class DogImageViewState : ViewState {
object Loading : DogImageViewState()
data class Content(val image: String) : DogImageViewState()
}
ХОРОШО
Итак, я создаю конкретную ViewModel для обмена логикой между iOS и Android.
class DogImageViewModel(initialState: DogImageViewState) : ViewModel<DogImageViewState>(initialState) {
private val interactor : GetDogImageUrlInteractor by dogDi.instance()
init {
getImage()
}
fun reload() {
updateState { DogImageViewState.Loading }
getImage()
}
private fun getImage() {
GlobalScope.launch(ioDispatcher) {
val image = interactor.getImageUrl()
updateState { (it as? Content)?.copy(image = image) ?: Content(image = image) }
}
}
}
Просто, понятно? Viewmodel инициализируется с загрузкой и сразу же начинает загрузку изображения. После загрузки изображение переходит в новое состояние. При обновлении действий точно так же
В Android использование очень простое, Jetpack Compose может
collectAsState
StateFlow и больше нечего делать, просто опишите ui с состоянием
Проблема в iOS
Мы должны «соединить» наблюдаемую модель для iOS. iOS имеет
ObservableObject
механизм и я расширил его
class ObservableViewModel<T : ViewState>: ObservableObject {
@Published var state : T
init(viewModel: ViewModel<T>) {
self.state = viewModel.currentState
viewModel.onChange { newState in
self.state = newState as! T
}
}
}
Выглядит неплохо. Общий класс для ViewState... Теперь я могу использовать его со SwiftUI! О нет...
Когда я пытаюсь использовать
ObservableViewModel
в коде я получаю ошибку
@StateObject var vm = ObservableViewModel<DogImageViewState>(viewModel: DogImageViewModel(initialState: DogImageViewState.Loading()))
Тип DogImageViewState не соответствует протоколу ViewState
Но точно соответствует !!!
Когда я перехожу к определению
DogImageViewState
я вижу дальше
__attribute__((swift_name("DogImageViewState")))
@interface DogDogImageViewState : DogBase <DogSharedViewState>
@end;
Похоже, совсем не соответствует. Что это
Base<ViewState>
(DogBase, потому что функция Dog строится в отдельной структуре
dog
и DogSharedViewState из-за ViewState, объявленного в
shared
)? Почему не просто
ViewState
?
Как объяснить быстрому, что DogSharedViewState и то же самое?