The Wayback Machine - https://web.archive.org/web/20230602173414/https://github.com/rickclephas/KMM-ViewModel
Skip to content

rickclephas/KMM-ViewModel

master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Code

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
December 22, 2022 16:04
April 28, 2023 13:03
May 19, 2023 13:39
November 28, 2022 22:39
November 27, 2022 17:02
May 19, 2023 13:46
May 19, 2023 13:39
May 19, 2023 13:39

KMM-ViewModel

A library that allows you to share ViewModels between Android and iOS.

Compatibility

The latest version of the library uses Kotlin version 1.8.21.
Compatibility versions for newer Kotlin versions are also available:

Version Version suffix Kotlin Coroutines AndroidX Lifecycle
latest no suffix 1.8.21 1.7.1 2.5.1
1.0.0-ALPHA-8 no suffix 1.8.21 1.7.0 2.5.1
1.0.0-ALPHA-7 no suffix 1.8.21 1.6.4 2.5.1
1.0.0-ALPHA-6 no suffix 1.8.20 1.6.4 2.5.1
1.0.0-ALPHA-4 no suffix 1.8.10 1.6.4 2.5.1
1.0.0-ALPHA-3 no suffix 1.8.0 1.6.4 2.5.1

Kotlin

Add the library to your shared Kotlin module:

dependencies {
    api("com.rickclephas.kmm:kmm-viewmodel-core:<version>")
}

And create your ViewModels:

import com.rickclephas.kmm.viewmodel.KMMViewModel
import com.rickclephas.kmm.viewmodel.MutableStateFlow
import com.rickclephas.kmm.viewmodel.stateIn

open class TimeTravelViewModel: KMMViewModel() {

    private val clockTime = Clock.time

    /**
     * A [StateFlow] that emits the actual time.
     */
    val actualTime = clockTime.map { formatTime(it) }
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), "N/A")

    private val _travelEffect = MutableStateFlow<TravelEffect?>(viewModelScope, null)
    /**
     * A [StateFlow] that emits the applied [TravelEffect].
     */
    val travelEffect = _travelEffect.asStateFlow()
}

As you might notice it isn't much different from an Android ViewModel.
The most obvious difference is the KMMViewModel superclass:

- import androidx.lifecycle.ViewModel
+ import com.rickclephas.kmm.viewmodel.KMMViewModel

- open class TimeTravelViewModel: ViewModel() {
+ open class TimeTravelViewModel: KMMViewModel() {

Besides that there are only 2 minor differences.
The first being a different import for stateIn:

- import kotlinx.coroutines.flow.stateIn
+ import com.rickclephas.kmm.viewmodel.stateIn

        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), "N/A")

And the second being a different MutableStateFlow constructor:

- import kotlinx.coroutines.flow.MutableStateFlow
+ import com.rickclephas.kmm.viewmodel.MutableStateFlow

-    private val _travelEffect = MutableStateFlow<TravelEffect?>(null)
+    private val _travelEffect = MutableStateFlow<TravelEffect?>(viewModelScope, null)

These minor differences will make sure that any state changes are propagate to iOS.

Note: viewModelScope is a wrapper around the actual CoroutineScope which can be accessed via the ViewModelScope.coroutineScope property.

KMP-NativeCoroutines

I highly recommend you to use the @NativeCoroutinesState annotation from KMP-NativeCoroutines to turn your StateFlows into properties in Swift:

@NativeCoroutinesState
val travelEffect = _travelEffect.asStateFlow()

Checkout the KMP-NativeCoroutines README for more information and installation instructions.

Alternative

Alternatively you can create extension properties in your iOS source-set yourself:

val TimeTravelViewModel.travelEffectValue: TravelEffect?
    get() = travelEffect.value

Android

Use the view model like you would any other Android view model:

class TimeTravelFragment: Fragment(R.layout.fragment_time_travel) {
    private val viewModel: TimeTravelViewModel by viewModels()
}

Note: improved support for Jetpack Compose is coming soon.

Swift

Add the Swift package to your Package.swift file:

dependencies: [
    .package(url: "https://github.com/rickclephas/KMM-ViewModel.git", from: "<version>")
]

Or add it in Xcode by going to File > Add Packages... and providing the URL: https://github.com/rickclephas/KMM-ViewModel.git.

CocoaPods

If you like you can also use CocoaPods instead of SPM:

pod 'KMMViewModelSwiftUI', '<version>'

Create a KMMViewModel.swift file with the following contents:

import KMMViewModelCore
import shared // This should be your shared KMM module

extension Kmm_viewmodel_coreKMMViewModel: KMMViewModel { }

After that you can use your view model almost as if it were an ObservableObject.
Just use the view model specific property wrappers and functions:

ObservableObject KMMViewModel
@StateObject @StateViewModel
@ObservedObject @ObservedViewModel
@EnvironmentObject @EnvironmentViewModel
environmentObject(_:) environmentViewModel(_:)

E.g. to use the TimeTravelViewModel as a StateObject:

import SwiftUI
import KMMViewModelSwiftUI
import shared // This should be your shared KMM module

struct ContentView: View {
    @StateViewModel var viewModel = TimeTravelViewModel()
}

It's also possible to subclass your view model in Swift:

import Combine
import shared // This should be your shared KMM module

class TimeTravelViewModel: shared.TimeTravelViewModel {
    @Published var isResetDisabled: Bool = false
}