Hey guys, what do you think is better for sharing ...
# multiplatform
d
Hey guys, what do you think is better for sharing viewmodels in KMM and why, moko-mvvm or kmm-viewmodel?
s
I haven't bothered with either, rolling your own is easy
👍 1
Copy code
// Common
expect open class ViewModel constructor(scope: CoroutineScope? = null) {
    val viewModelScope: CoroutineScope
    protected open fun onCleared()
    protected open fun performSetup()
}

// Android

import androidx.lifecycle.ViewModel as AndroidXViewModel
import androidx.lifecycle.viewModelScope as androidXViewModelScope
actual open class ViewModel actual constructor(scope: CoroutineScope?) : AndroidXViewModel() {
    actual val viewModelScope: CoroutineScope = scope ?: androidXViewModelScope

    init {
        viewModelScope.launch {
            performSetup()
        }
    }

    actual override fun onCleared() {
        super.onCleared()
    }

    protected actual open fun performSetup() {
        /* Perform any setup here rather than in init block */
    }

    actual interface StateWithGenericError {
        actual interface ErrorState {
            actual val state: GenericErrorState?
        }
    }
}

// iOS
/**
 * Base class that provides a Kotlin/Native equivalent to the AndroidX `ViewModel`. In particular, this provides
 * a [CoroutineScope][kotlinx.coroutines.CoroutineScope] which uses [Dispatchers.Main][kotlinx.coroutines.Dispatchers.Main]
 * and can be tied into an arbitrary lifecycle by calling [clear] at the appropriate time.
 */
actual open class ViewModel actual constructor(scope: CoroutineScope?) {
    actual val viewModelScope = scope ?: MainScope()

    /**
     * Override this to do any cleanup immediately before the internal [CoroutineScope][kotlinx.coroutines.CoroutineScope]
     * is cancelled in [clear]
     */
    protected actual open fun onCleared() {
        /* Default No-Op */
    }

    /**
     * Cancels the internal [CoroutineScope][kotlinx.coroutines.CoroutineScope]. After this is called, the ViewModel should
     * no longer be used.
     */
    fun clear() {
        onCleared()
        viewModelScope.cancel()
    }

    /**
     * Create a [FlowAdapter] from this [Flow] to make it easier to interact with from Swift.
     */
    fun <T : Any> Flow<T>.asCallbacks() = FlowAdapter(viewModelScope, this)

    protected actual open fun performSetup() {
        /* Overridden where needed */
    }
}

class FlowAdapter<T : Any>(
    private val scope: CoroutineScope,
    private val flow: Flow<T>
) {
    fun subscribe(
        onEach: (item: T) -> Unit,
        onComplete: () -> Unit = {},
        onThrow: (error: Throwable) -> Unit = {}
    ): Canceller = JobCanceller(
        flow.onEach { onEach(it) }
            .catch { onThrow(it) }
            .onCompletion { onComplete() }
            .launchIn(scope)
    )
}

interface Canceller {
    fun cancel()
}

private class JobCanceller(private val job: Job) : Canceller {
    override fun cancel() {
        job.cancel()
    }
}
🐕 1
🤔 1
Literally all you need
🙌🏻 1
🙌 5
t
I have used the second one. Work well
j
Have also been very happy with KMM-ViewModel
d
I have also been using it up until now though am seeing moko-mvvm being kinda popularized with occurrence of iOS’s alpha version of compose multiplatform. Jetbrains used it in one of the tutorials

here

alongside this fella that kinda has a big following

here

, so am thinking if it has an advantage for compose multiplatform wdyt?
e
k
started with moko but there are so many extensions that you have to write for making everything, e.g., flows to work. However, that comes by default with kmm viewmodel and native coroutines which makes development very easy to start. if using voyager screen model is the go to thing
f
Decompose is also an option. It also has navigation. Worth researching!
1
576 Views