Is it possible to use interfaces of ViewModel in Swift?
olegivo opened this issue · comments
olegivo commented
Interfaces are useful in case of faking in previews.
For example:
interface ItemViewModel {
val title: CMutableStateFlow<String>
val isEditing: CStateFlow<Boolean>
val canSave: CStateFlow<Boolean>
}
class ItemViewModelImpl(
private val heavyDependency1: HeavyDependency1,
private val heavyDependency2: HeavyDependency2,
...
private val heavyDependency101: HeavyDependency101,
): ViewModel(), ItemViewModel {
override val title = MutableStateFlow("").cMutableStateFlow()
override val isEditing = MutableStateFlow(false).cMutableStateFlow()
override val canSave = MutableStateFlow(false).cMutableStateFlow()
}
class ItemViewModelFake(
title: String,
isEditing: Boolean = false,
canSave: Boolean = false,
): ViewModel(), ItemViewModel {
override val title = MutableStateFlow(title).cMutableStateFlow()
override val isEditing = MutableStateFlow(isEditing).cStateFlow()
override val canSave = MutableStateFlow(canSave).cStateFlow()
}
@Composable
fun Item(viewModel: ViewModel /*for the sake of simplicity without DI*/) {
// some UI
}
And use them for previews:
@Preview
@Composable
private fun ItemPreview() {
MaterialTheme {
Item(
ViewModelFake(title = "123")
)
}
}
@Preview
@Composable
private fun ItemEditingCannotSavePreview() {
MaterialTheme {
Item(
ViewModelFake(title = "123", isEditing = true)
)
}
}
@Preview
@Composable
private fun ItemEditingCanSavePreview() {
MaterialTheme {
Item(
ViewModelFake(title = "123", isEditing = true, canSave = true)
)
}
}
But not for SwiftUI. We can't make extensions for protocols (to be ObservableObject).
Darron Schall commented
You can support this in SwiftUI with generics. Something like this (apologies if this doesn't compile, writhing this code directly in a comment):
struct ItemView<Model>: View where Model: ItemViewModel & ViewModel {
@ObservedObject var viewModel: Model
var body: some View {
Text(viewModel.state(\.title))
}
}
struct ItemView_Previews: PreviewProvider {
static var previews: some View {
ItemView<ItemViewModelFake>(
viewModel: ItemViewModelFake()
)
}
}