Do you think the viewmodels should be on kmm or sh...
# multiplatform
m
Do you think the viewmodels should be on kmm or should it be on native ?
i
Native, IMO
👍 1
m
I would like to know the reason for it if you know for example, if I use viewmodels in kmm, what difficulties would I have?
j
shared if screens are really similar
t
I view it as right on the border between the two. I have used either plain old callbacks from the VM to the native client, or use Flow with some common wrappers. Something like this:
Copy code
class BookViewModel {
    private val _loading = MutableStateFlow(false)
    val loading = _loading.asStateFlow()
    val nativeLoading = _loading.asFlowWrapper()
}
The Android side subscribes to .loading, and the iOS will use the .nativeLoading with either an RxSwift or Combine wrapper (depending on min iOS version). Then in the kmm common code, we just set _loading.value to some state, and it gets sent where it needs to go...
3
c
VMs absolutely in KMM for me
💯 4
d
definitely should be in common module
m
Thanks for your opinions 👍
r
I just created an abstract class called CommonViewModel inside commonMain with expect/actual for each platform. It has a coroutine scope and an open function to destroy things on cleanup. The one for Android directly inherits ViewModel, while the one for pure jvm just cleans up the scope and I can call destroy on the platform code. Since I use Decompose for navigation on jvm (I use the regular Android nav library for Android only) and use Koin for DI, I did this: • Helper extension function for scope called
commonViewModel{}
for Koin, so that I just provide my ViewModels from
commonMain
• Helper extension function
commonViewModel()
for injecting the dependencies in the platform UI. In Android it just wraps
viewModel()
functions provided by Koin. In jvm with Decompose it is an extension of
ComponentContext
and will call the
destroy()
function I mentioned earlier when
lifecycle.onDestroy
is reached and automatically cleans up resources (like cancelling the scope). Has worked beautifully so far and does not need me to add another library (like Mokko MVVM) where you'd have to deal with some platforms that aren't supported like jvm and js. If you add another platform you just add it's implementation of the ViewModel (or wrap the platforms existing code) and you are done The only major downside is that you'd have to manually call the destroy () function if you are not using Koin or Decompose and the platform has no way of clearing them automatically. I'll post the link to an open source project (when I get home) where I use this approach so you can review it yourself
K 1
m
@Racka N thank you very much. I created mvvm structure ( data, domain, presentation, etc. ) on kmm commonModule with moko and using koin as a DI. I test one of viewmodel both of kotlin and swift application and its works i just needed to wrapper class for MutableStateFlow objects to use it on swift. So now our ios and android developers needs to install the kmm dependency with jitpack.io and swift package manager and creating compose or swift ui view then initialize the view model which comes from kmm and coding only ui layer. That was my dream. Now it’s really happening right now, I asked this question to see if there would be a problem on the way we are going.
And also i want to view your project too 👍
r
Here's the link: https://github.com/Thinkrchive/Thinkrchive-Multiplatform Look at the
working-kotlin-js-support
branch. It's the one that's the most up to date. Also ignore the
:common:features
module. Everything in there has been moved to the
:common:all-features
module after I added the commonViewModel stuff.
i
Yeah, sorry - I meant to elaborate other than just saying "native" haha. For me, that interface to KMM module are UseCases (e.g. SignInUseCase, GetUserProfileUseCase, etc.). I prefer to keep view models native, just because I think it's easier for Android, to leverage that magical ViewModel (
androidx.lifecycle.ViewModel
), unless I'm missing something and there's a better way. With iOS it's easy, as you can instantiate the view model in a view controller, or a coordinator, depending on which architecture you like. Also, I feel like view models, although they contain logic, this is just presentational logic. All the business logic remains in the domain, which lives in the shared module. Hope this makes sense!
j
you can use expect actual and the actual in android will be the lifecycle viewmodel, but the expect class will be common, getting both, a shared viewmodel with lifecycle viewmodel behavior in android
i
Yeah, that works too. I just live under the assumption that there will be some disparity between iOS and Android and I won't be able to use the same view model 100%. Anyhow, very minor decision IMO, when you take into account that majority of work is in the domain/data layers
👍 1
k
Kind of a mix. Simply put, the only thing we really like having the android ViewModel (capital V) for is lifecycle. Otherwise, the view model (lower case v) logic is in shared (at least the parts that aren't android specific, if any). We do an expect/actual for the scope in kampkit https://github.com/touchlab/KaMPKit/blob/main/shared/src/commonMain/kotlin/co/touchlab/kampkit/models/ViewModel.kt. internally we have a doc on 2 different approaches and when we use each. May open that up some day
Curious what people think of the android lifecycle and ViewModel if using compose and disabling the context changes. I haven't done much of that yet but been hearing lots of discussions
a
When using Compose together with compose-navigation, the ViewModel has additional purpose - it lives while the screen is in the back stack. When using Decompose - there is no such thing as ViewModel.
r
What if you use compose-navigation on an Android source but Decompose on Desktop?
a
Then I believe you still need ViewModel on Android
j
There is edge cases in which disabling all context changes still don't fix issues that the ViewModel fixes, and having the expect actual is just a few lines of code, so I think there is no problem to keep it until those edge cases get fixed