rickclephas / KMP-ObservableViewModel

Library to use AndroidX/Kotlin ViewModels with SwiftUI
MIT License
569 stars 29 forks source link

Is it possible to use interfaces of ViewModel in Swift? #35

Closed olegivo closed 1 year ago

olegivo commented 1 year ago

Interfaces are useful in case of faking in previews. For example:


interface ItemViewModel {
    @NativeCoroutinesState val title: MutableStateFlow<String>
    @NativeCoroutinesState val isEditing: StateFlow<Boolean>
    @NativeCoroutinesState val canSave: StateFlow<Boolean>
}

class ItemViewModelImpl(
    private val heavyDependency1: HeavyDependency1,
    private val heavyDependency2: HeavyDependency2,
    ...
    private val heavyDependency101: HeavyDependency101,
): KMMViewModel(), ItemViewModel {
    override val title = MutableStateFlow("")
    override val isEditing = MutableStateFlow(false)
    override val canSave = MutableStateFlow(false)
}

class ItemViewModelFake(
    title: String,
    isEditing: Boolean = false,
    canSave: Boolean = false,
): KMMViewModel(), ItemViewModel {
    override val title = MutableStateFlow(title)
    override val isEditing = MutableStateFlow(isEditing)
    override val canSave = MutableStateFlow(canSave)
}

@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)
        )
    }
}

What about SwiftUI?

rickclephas commented 1 year ago

That likely depends on your SwiftUI code. It probably expects an ObservableObject, which means you would need to use a KMMViewModel type. So I guess the best way would be to convert your ItemViewModel to an abstract class. Allowing it to be a subclass of KMMViewModel.

Note: in order for state changes to propagate to Swift you should use the KMMViewModel constructor for MutableStateFlow:

override val title = MutableStateFlow(viewModelScope, "")
olegivo commented 1 year ago

@rickclephas , thank you very much! This approach works ❤️